/*
* Conditions Of Use
*
* This software was developed by employees of the Sigmatrix(Beijing) Corporation.
* This software is provided by sigmatrix as a service and is expressly
* provided "AS IS."  Sigmatrix MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
* OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
* AND DATA ACCURACY.  Sigmatrix does not warrant or make any representations
* regarding the use of the software or the results thereof, including but
* not limited to the correctness, accuracy, reliability or usefulness of
* the software.
*
* Permission to use this software is contingent upon your acceptance
* of the terms of this agreement.
*
*/
package com.sigmatrix.h5.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sigmatrix.h5.bo.InStorageNumBo;
import com.sigmatrix.h5.bo.InStorageUpLoadBo;
import com.sigmatrix.h5.bo.InStorageUpLoadDetailBo;
import com.sigmatrix.h5.bo.OperateCodeUpLoadBo;
import com.sigmatrix.h5.dao.DisDealerAcctDao;
import com.sigmatrix.h5.dao.DisDealerDao;
import com.sigmatrix.h5.dao.EnterpriseDao;
import com.sigmatrix.h5.dao.FirstLevelQrCodeDao;
import com.sigmatrix.h5.dao.InStorageDao;
import com.sigmatrix.h5.dao.InStorageDetailDao;
import com.sigmatrix.h5.dao.InStorageTerminalUserDao;
import com.sigmatrix.h5.dao.MultistageLevelQrCodeDao;
import com.sigmatrix.h5.dao.ProductDao;
import com.sigmatrix.h5.dao.WarehouseDao;
import com.sigmatrix.h5.dao.WarehouseStockDao;
import com.sigmatrix.h5.dao.WarehouseStockDetailsDao;
import com.sigmatrix.h5.dto.EnterpriseData;
import com.sigmatrix.h5.dto.InStorageData;
import com.sigmatrix.h5.dto.InStorageDetailData;
import com.sigmatrix.h5.dto.ProductData;
import com.sigmatrix.h5.dto.QRCodeError;
import com.sigmatrix.h5.dto.WarehouseData;
import com.sigmatrix.h5.entity.CodeCirculation;
import com.sigmatrix.h5.entity.DisDealer;
import com.sigmatrix.h5.entity.DisDealerAcct;
import com.sigmatrix.h5.entity.Enterprise;
import com.sigmatrix.h5.entity.FirstLevelQRCode;
import com.sigmatrix.h5.entity.InStorage;
import com.sigmatrix.h5.entity.InStorageDetail;
import com.sigmatrix.h5.entity.InStorageTerminalUser;
import com.sigmatrix.h5.entity.MultistageLevelQRCode;
import com.sigmatrix.h5.entity.Product;
import com.sigmatrix.h5.entity.SyncTask;
import com.sigmatrix.h5.entity.Warehouse;
import com.sigmatrix.h5.entity.WarehouseStock;
import com.sigmatrix.h5.entity.WarehouseStockDetails;
import com.sigmatrix.h5.enums.InStorageStatusEnum;
import com.sigmatrix.h5.enums.MultistageLevelQRCodeStatusEnum;
import com.sigmatrix.h5.enums.ResponseEnum;
import com.sigmatrix.h5.exception.DaoException;
import com.sigmatrix.h5.exception.ServiceException;
import com.sigmatrix.h5.message.bigdata.BigDataInStorage;
import com.sigmatrix.h5.message.bigdata.BigDataInStorageDetail;
import com.sigmatrix.h5.page.PageBean;
import com.sigmatrix.h5.properties.BeanConfig;
import com.sigmatrix.h5.response.Response;
import com.sigmatrix.h5.service.AdminAreaService;
import com.sigmatrix.h5.service.EnterpriseService;
import com.sigmatrix.h5.service.InStorageService;
import com.sigmatrix.h5.service.ProductService;
import com.sigmatrix.h5.service.SyncTaskService;
import com.sigmatrix.h5.service.common.impl.BaseServiceImpl;
import com.sigmatrix.h5.service.log.BigDataLogger;
import com.sigmatrix.h5.utils.CommonUtils;
import com.sigmatrix.h5.utils.Cons;
import com.sigmatrix.h5.utils.DateUtil;
import com.sigmatrix.h5.utils.ThreadPool;
import com.sigmatrix.h5.utils.Utils;
import com.sigmatrix.h5.web.mode.SigmatrixLocationWrapper;
import com.sigmatrix.h5.web.vo.BaseInStorageVo;
import com.sigmatrix.h5.web.vo.InStorageAddVo;
import com.sigmatrix.h5.web.vo.InStorageDetailAddVo;
import com.sigmatrix.h5.web.vo.InStorageListVo;
import com.sigmatrix.h5.web.vo.InStoragePageVo;
import com.sigmatrix.h5.web.vo.InStorageUpLoad;
import com.sigmatrix.h5.web.vo.InStorageUpLoadDetailVo;
import com.sigmatrix.h5.web.vo.InStorageUpLoadVo;
import com.sigmatrix.h5.web.vo.InStorageVo;
import com.sigmatrix.h5.web.vo.OperateCodeUpLoadVo;
import com.sigmatrix.h5.web.vo.QRCodeDetail;
import com.sigmatrix.h5.web.vo.QrCode;

/**
 * @ClassName: InStorageServiceImpl
 * @Description: 入库单业务层接口
 * @author youjun @date 2017-07-06 14:18:28
 */
@Service(value = "inStorageService")
public class InStorageServiceImpl extends BaseServiceImpl<InStorageData> implements InStorageService {
	
	/**logger record*/
	private static final Logger LOGGER = LoggerFactory.getLogger(InStorageServiceImpl.class);
	
	@Autowired
	private AdminAreaService adminAreaService;
	
	/**注入mongoTemplate进行mongo db的操作*/
	@Autowired
	protected transient MongoTemplate mongoTemplate;
	@Autowired
	private transient InStorageDao inStorageDao;
	@Autowired
	private transient WarehouseDao warehouseDao;
	@Autowired
	private transient DisDealerDao disDealersDao;  
	@Autowired
	private transient InStorageDetailDao inStorageDetailDao;
	@Autowired
	private transient ProductService productService;
	@Autowired
	private transient MultistageLevelQrCodeDao multistageLevelQRCodeDao;
	@Autowired
	private transient FirstLevelQrCodeDao firstLevelQRCodeDao;
	@Autowired
	private transient ProductDao productDao;
	@Autowired
	private transient WarehouseStockDao warehouseStockDao;
	@Autowired
	private transient WarehouseStockDetailsDao warehouseStockDetailsDao;
	@Autowired
	private transient DisDealerAcctDao disDealersAcctDao;
	@Autowired
	private transient InStorageTerminalUserDao inStorageTerminalUserDao;
	@Autowired
	private transient InStorageService inStorageService;
	@Autowired
	private transient EnterpriseDao enterpriseDao;
	@Autowired
	private transient SyncTaskService syncTaskService;
	@Autowired
	private transient EnterpriseService enterpriseService;
	@Autowired
	private transient BeanConfig beanConfig;
	/**入库渠道类型,1-生产入库*/
	public static final int IN_CHANNEL_TYPE_PRO = 1;
	
	/**能执行上传入库单操作的状态：已审核、进行中*/
	private static List<Integer> canUpdateStatusList = new ArrayList<Integer>();
	
	/**能执行关闭入库单操作的状态:进行中、有异常*/
	private static List<Integer> canCloseStatusList = new ArrayList<Integer>();
	
	/**能执行关闭入库单详情单操作的状态:进行中、有异常*/
	private static List<Integer> canCloseDetailStatusList = new ArrayList<Integer>();
	
	/**多级码已经入库的状态集合*/
	private static List<Integer> alreadyInStorageStatusList = new ArrayList<Integer>();
	
	static {
		canUpdateStatusList.add(InStorageStatusEnum.HAS_AUDIT.getId()); // 已审核
		canUpdateStatusList.add(InStorageStatusEnum.PROCEED_ING.getId()); // 进行中
		
		alreadyInStorageStatusList.add(MultistageLevelQRCodeStatusEnum.IN_STORAGE.getId());
		
		canCloseStatusList.add(InStorageStatusEnum.PROCEED_ING.getId()); // 进行中
		canCloseStatusList.add(InStorageStatusEnum.EXCEPTION.getId()); // 有异常
		canCloseStatusList.add(InStorageStatusEnum.HAS_AUDIT.getId()); // 已审核
		
		canCloseDetailStatusList.add(InStorageStatusEnum.HAS_AUDIT.getId()); // 已审核
		canCloseDetailStatusList.add(InStorageStatusEnum.PROCEED_ING.getId()); // 进行中
		canCloseDetailStatusList.add(InStorageStatusEnum.EXCEPTION.getId()); // 有异常
	}
	
	/**组织类型:1-企业*/
	public static final int ORGANIZATION_TYPE_ENT = 1;
	
	/**组织类型:2-经销商*/
	public static final int ORGANIZATION_TYPE_DEALER = 2;

	/**
	 * (non-Javadoc)
	 *@Title: findListByPage
	 *@Description: 
	 *@see com.sigmatrix.sc.handheld.inter.service.InStorageService#findListByPage(com.sigmatrix.sc.handheld.inter.page.PageBean, com.sigmatrix.sc.handheld.inter.web.vo.InStorageVo, java.util.Map)
	 *@author youjun
	 *@date 2017年7月6日 下午4:12:12
	 */
	@Override
	public Response findListByPage(final PageBean<InStorageData> pageBean, final InStoragePageVo inStorageVo,
			final Map<String, Object> map) throws ServiceException {
		try {
			final String eseCode = inStorageVo.getEnterpriseCode();
			final InStorage inStorage = new InStorage();
			BeanUtils.copyProperties(inStorageVo, inStorage);
			inStorage.setDealerId(inStorageVo.getUserDealerId());//添加经销商id
			//设置查询条件放入map中，实体中没有的  开始时间和结束时间
			final Map<String, Object> otherParamsMap = new HashMap<String, Object>();
			final String createTimeStart = inStorageVo.getCreateTimeStart();
			final String createTimeEnd = inStorageVo.getCreateTimeEnd();
			if(StringUtils.isNotBlank(createTimeStart)) {
				otherParamsMap.put("createTimeStart", createTimeStart);
			}
			if(StringUtils.isNotBlank(createTimeEnd)&&createTimeEnd.length()>=10) {
				final StringBuilder createTimeEndSb = new StringBuilder();
				createTimeEndSb.append(createTimeEnd.substring(0, 10));
				createTimeEndSb.append(" ").append("23:59:59");
				otherParamsMap.put("createTimeEnd", createTimeEndSb.toString());
			}
			//分页查询主入库单信息
//			final List<InStorage> list = 
//					inStorageDao.findByPage(pageBean.getPageNo(), pageBean.getPageSize(),inStorage, otherParamsMap, eseCode);
//			final int total = inStorageDao.getCountByCondition(inStorage, otherParamsMap, eseCode);
			
			final List<InStorage> list = 
					inStorageDao.findByPage(pageBean.getPageNo(), pageBean.getPageSize(),inStorage, otherParamsMap, eseCode,inStorageVo.getUserId());
			final int total = inStorageDao.getCountByCondition(inStorage, otherParamsMap, eseCode,inStorageVo.getUserId());
			
			List<InStorageData> dataList = convertDataList(list);
			//查询明细
			dataList = fillInStorageDto(eseCode, dataList);
			//转化为page对象
			convertPageBean(dataList, total, pageBean);
			return new Response(ResponseEnum.SUCCESS, pageBean);
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

	/**
	 * @Title: convertDataList
	 * @Description: 转换实体bean和页面展示数据bean
	 * @return List<InStorageData>
	 * @author youjun 
	 * @date 2017-07-06 14:18:28
	 * @param list
	 * @return
	 * @throws ServiceException
	 */
	private List<InStorageData> convertDataList(final List<InStorage> list) throws ServiceException {

		final List<InStorageData> dataList = new ArrayList<InStorageData>();
		if (!CollectionUtils.isEmpty(list)) {
			for (final InStorage item : list) {
				final InStorageData data = new InStorageData();
				BeanUtils.copyProperties(item, data);
				data.setInStorageId(item.getId());
				// 处理日期
				if(Utils.isNotEmpty(item.getCreateTime())) {
					data.setCreateTime(DateUtil.getDateTimeStr(item.getCreateTime()));
				}
				data.setInStorageId(item.getId());
				dataList.add(data);
			}
		}
		return dataList;
	}

	/**
	 * (non-Javadoc)
	 *@Title: findById
	 *@Description: 
	 *@see com.sigmatrix.sc.handheld.inter.service.InStorageService#findById(com.sigmatrix.sc.handheld.inter.request.Request, java.lang.Integer)
	 *@author youjun
	 *@date 2017年7月8日 下午5:25:20
	 */
	@Override
	public Response findById(final String eseCode, final Long inStorageId) throws ServiceException {
		LOGGER.info("传入库单明细 eseCode={} inStorageId={}",eseCode,inStorageId);
		try {
			//查询主入库单
			final InStorage inStorage = inStorageDao.findById(inStorageId,eseCode);
			if (Utils.isEmpty(inStorage)) {
				return new Response(ResponseEnum.NO_DATA, "该入库单不存在");
			}
			//主入库单转换为dto
			final InStorageData data = new InStorageData();
			BeanUtils.copyProperties(inStorage, data);
			data.setInStorageId(inStorage.getId());
			final Date createTime = inStorage.getCreateTime();
			if(Utils.isNotEmpty(createTime)) {//创建时间转换为字符串
				data.setCreateTime(DateUtil.getDateTimeStr(createTime));
			}
			
			//主入库单放入list中
			final List<InStorageData> inStorageList = new ArrayList<InStorageData>();
			inStorageList.add(data);
			
			//为主入库单设置明细
			final List<InStorageData> resultList = fillInStorageDto(eseCode, inStorageList);
			if(Utils.isEmpty(resultList)) {
				throw new ServiceException("为主入库单设置明细失败");
			}else{
				return new Response(ResponseEnum.SUCCESS, resultList.get(0));
			}
		} catch (DaoException e) {
			throw new ServiceException(e);
		}
	}

	/**
	 * (non-Javadoc)
	 *@Title: save
	 *@Description: 
	 *@see com.sigmatrix.h5.service.InStorageService#save(com.sigmatrix.h5.web.vo.InStorageAddVo)
	 *@author 
	 *@date 2017年9月12日 上午11:42:04
	 */
	@Override
	@Transactional(rollbackFor=ServiceException.class)
	public Response save(final InStorageAddVo inStorageVo) throws ServiceException {
		final String eseCode = inStorageVo.getEnterpriseCode();
		try {
			//主入单
			InStorage inStorage = new InStorage();
			BeanUtils.copyProperties(inStorageVo, inStorage);
			// 先行验证 "入库单据号"不能重复
			InStorage inStorageTemp = inStorageDao.findByOrderCode(inStorage.getOrderCode(), eseCode);
			if(Utils.isNotEmpty(inStorageTemp)){
				return new Response(Cons.RESPONSE_CODE_EXISTED_DATA, "单据已存在");
			}
			//默认状态为已经审核
			inStorage.setStatus(InStorageStatusEnum.HAS_AUDIT.getId().shortValue());
			inStorage.setCreatorId(inStorageVo.getUserId());
			inStorage.setCreateTime(new Date());
			inStorage.setDealerId(inStorageVo.getUserDealerId());
			
			//设置主入库单计划数量
			this.setPlanNum(inStorageVo, inStorage);
			
			//如果传递仓库为空，给与添加一个默认仓库
			if (Utils.isEmptyZero(inStorageVo.getWarehouseId())) {
				//根据经销商查询仓库
				final List<WarehouseData> warehouseList = 
						warehouseDao.findPartByDealerIdDefault(eseCode, inStorageVo.getUserDealerId());
				if (Utils.isNotEmpty(warehouseList)) {
					WarehouseData warehouseData = warehouseList.get(0);
					inStorageVo.setWarehouseId(warehouseData.getWarehouseId());
					inStorage.setWarehouseId(warehouseData.getWarehouseId());
				}
			}
			
			//保存主入库单
			final Long instorageId = (long) inStorageDao.save(inStorage, eseCode);
			if(instorageId<=0) {
				throw new ServiceException("创建主入库单失败");
			}
			inStorage.setId(instorageId);//主入库单id
			
			//设置主入库单明细信息
			final List<InStorageDetailAddVo> detailVoList = inStorageVo.getDetailList();//请求入库单详情
			final Integer userId = inStorageVo.getUserId();
			final List<InStorageDetail> detailList = filterAndFillDetailList(eseCode,userId,detailVoList,inStorage);
			// 保存入库单详情
			int result = inStorageDetailDao.batchSave(detailList, eseCode);
			if (result <= 0) {
				throw new ServiceException("创建入库单明细失败");
			}
			
			//保存入库单绑定用户
			InStorageTerminalUser inStorageTerminalUser = new InStorageTerminalUser();
			inStorageTerminalUser.setInStorageId(Integer.valueOf(String.valueOf(instorageId)));
			inStorageTerminalUser.setTerminalUserId(inStorageVo.getUserId());
			inStorageTerminalUserDao.save(inStorageTerminalUser, eseCode);
			
			//查询返回的实体
			return findById(eseCode,new Long(instorageId));
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}
	
	/**
	 * (non-Javadoc)
	 *@Title: saveAuto
	 *@Description: 
	 *@see com.sigmatrix.h5.service.InStorageService#saveAuto(com.sigmatrix.h5.web.vo.InStorageAddVo)
	 *@author gaomingkai
	 *@date 2018年1月19日 下午2:51:03
	 */
	@Override
	@Transactional(rollbackFor=ServiceException.class)
	public Response saveAuto(InStorageAddVo inStorageVo) throws ServiceException {
		final String eseCode = inStorageVo.getEnterpriseCode();
		try {
			//主入单
			InStorage inStorage = new InStorage();
			BeanUtils.copyProperties(inStorageVo, inStorage);
			// 先行验证 "入库单据号"不能重复
			InStorage inStorageTemp = inStorageDao.findByOrderCode(inStorage.getOrderCode(), eseCode);
			if(Utils.isNotEmpty(inStorageTemp)){
				return new Response(Cons.RESPONSE_CODE_EXISTED_DATA, "单据已存在");
			}
			//默认状态为已经审核
			//inStorage.setStatus(InStorageStatusEnum.HAS_AUDIT.getId().shortValue());
			//inStorage.setCreatorId(inStorageVo.getUserId());  //自动入库用户id没有传
			inStorage.setCreateTime(new Date());
			inStorage.setDealerId(inStorageVo.getUserDealerId());
			inStorage.setInStorageType(inStorageVo.getInStorageType());
//			inStorage.setInStorageType(Cons.IN_STORAGE_TYPE_PDA);
			//设置主入库单计划数量
			this.setPlanNum(inStorageVo, inStorage);
			
			//如果传递仓库为空，给与添加一个默认仓库
			if (Utils.isEmptyZero(inStorageVo.getWarehouseId())) {
				//根据经销商查询仓库
				final List<WarehouseData> warehouseList = 
						warehouseDao.findPartByDealerIdDefault(eseCode, inStorageVo.getUserDealerId());
				if (Utils.isNotEmpty(warehouseList)) {
					WarehouseData warehouseData = warehouseList.get(0);
					inStorageVo.setWarehouseId(warehouseData.getWarehouseId());
					inStorage.setWarehouseId(warehouseData.getWarehouseId());
				}
			}
			
			//保存主入库单
			final Long instorageId = (long) inStorageDao.save(inStorage, eseCode);
			if(instorageId<=0) {
				throw new ServiceException("创建主入库单失败");
			}
			inStorage.setId(instorageId);//主入库单id
			
			//设置主入库单明细信息
			final List<InStorageDetailAddVo> detailVoList = inStorageVo.getDetailList();//请求入库单详情
			final Integer userId = inStorageVo.getUserId();
			final List<InStorageDetail> detailList = filterAndFillDetailList(eseCode,userId,detailVoList,inStorage);
			// 保存入库单详情
			int result = inStorageDetailDao.batchSave(detailList, eseCode);
			if (result <= 0) {
				throw new ServiceException("创建入库单明细失败");
			}
			//查询返回的实体
			return findById(eseCode,new Long(instorageId));
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}
	/**
	 *@Title: setPlanNum
	 *@Description: 为主入库单设置计划数量
	 *@return InStorage
	 *@date 2017年9月18日 下午5:17:09
	 *@param inStorageVo 请求vo
	 *@param inStorage 主入库单
	 */
	private InStorage setPlanNum(final InStorageAddVo inStorageVo, final InStorage inStorage) {
		if(inStorageVo!=null||inStorage!=null) {
			//入库单明细
			final List<InStorageDetailAddVo> detailList = inStorageVo.getDetailList();
			if(!CollectionUtils.isEmpty(detailList)) {
				int planNum = 0;//计划包装数
				int planSpNum = 0;//计划单品数
				for(final InStorageDetailAddVo detailVo:detailList) {
					planNum+=detailVo.getPlanNum()!=null?detailVo.getPlanNum():0;
					planSpNum+=detailVo.getPlanSpNum()!=null?detailVo.getPlanSpNum():0;
				}
				inStorage.setPlanNumber(planNum);
				inStorage.setPlanSpNum(planSpNum);
			}
		}
		return inStorage;
	}
	
	/**
	 *@Title: filterAndFillDetailList
	 *@Description: 过滤错误的入库详情单并填充入库单信息(入库单ID、入库单号等)
	 *@return List<InStorageDetail>
	 *@date 2017年9月18日 上午11:40:53
	 *@param eseCode
	 *@param detailVoList 入库单详情
	 *@param inStorage 主入库单
	 *@throws ServiceException
	 */
	private List<InStorageDetail> filterAndFillDetailList(final String eseCode,final Integer userId,
			final List<InStorageDetailAddVo> detailVoList,final InStorage inStorage) throws ServiceException {
		
		if(Utils.isEmpty(inStorage)||Utils.isEmpty(inStorage.getOrderCode())) {
			throw new ServiceException("入库单信息不能为空");
		}
		if(!CollectionUtils.isEmpty(detailVoList)) {
			final List<InStorageDetail> detailList = new ArrayList<InStorageDetail>();
			for(final InStorageDetailAddVo vo:detailVoList) {
				//查询产品包装信息
				Integer packLevel = null;//包装级别
				String packLevelName = null;//包装级别名称
				Integer spLevel = Cons.PACK_LEVEL_ONE;//单品级别
				String spLevelName = null;//单品级别名称
				final Date nowDate = new Date();
				final Integer creatorId = userId;
				final ProductData proDto = productService.findById(vo.getProductId(), eseCode);
				if(Utils.isEmpty(proDto)||Utils.isEmpty(proDto.getProductUnit())){
					LOGGER.error("产品信息|包装级别 为空eseCode={} proId={}",eseCode,vo.getProductId());
					throw new ServiceException("产品信息为空");
				}
				packLevel = new Integer(proDto.getProductUnit());//包装级别
				packLevelName = proDto.getPackUnit();//包装名称
				final Map<Integer,String> packStandardMap = proDto.getPackStandardMap();
				if(Utils.isNotEmpty(packStandardMap)){
					spLevelName = packStandardMap.get(spLevel);//单品级别名称
				}
				//包装详情
				final InStorageDetail detailPack = new InStorageDetail();
				detailPack.setProductId(proDto.getProductId());
				detailPack.setProductCode(proDto.getProductCode());
				detailPack.setOrderCode(inStorage.getOrderCode());
				detailPack.setInStorageId(inStorage.getId());
				detailPack.setStatus(InStorageStatusEnum.HAS_AUDIT.getId().shortValue());//默认已审核
				detailPack.setCodeLevel(packLevel);
				detailPack.setWrapName(packLevelName);
				detailPack.setCreateTime(nowDate);
				detailPack.setCreatorId(creatorId);
				detailPack.setPlanNum(vo.getPlanNum());//包装数量
				
				//单品详情
				final InStorageDetail detailSp = new InStorageDetail();
				detailSp.setProductId(proDto.getProductId());
				detailSp.setProductCode(proDto.getProductCode());
				detailSp.setOrderCode(inStorage.getOrderCode());
				detailSp.setInStorageId(inStorage.getId());
				detailSp.setStatus(InStorageStatusEnum.HAS_AUDIT.getId().shortValue());//默认已审核
				detailSp.setCodeLevel(spLevel);
				detailSp.setWrapName(spLevelName);
				detailSp.setCreateTime(nowDate);
				detailSp.setCreatorId(creatorId);
				detailSp.setPlanNum(vo.getPlanSpNum());//单品数量
				
				detailList.add(detailPack);
				detailList.add(detailSp);
			}
			return detailList;
		}
		return null;
	}

	/**
	 * (non-Javadoc)
	 *@Title: findListByCondition
	 *@Description: 
	 *@see com.sigmatrix.sc.handheld.inter.service.InStorageService
	 *	#findListByCondition(com.sigmatrix.sc.handheld.inter.web.vo.InStorageVo, boolean)
	 *@author youjun
	 *@date 2017年7月7日 下午4:10:05
	 */
	@Override
	public Response findListByCondition(final InStorageVo inStorageVo, final boolean isQueryDetail) throws ServiceException {

		final Response validateBasicParam = validateBasicParams(inStorageVo);
		if(validateBasicParam.getCode()!=Cons.RESPONSE_CODE_SUCCESS) {
			return validateBasicParam;
		}
		final List<InStorageData> dataList = new ArrayList<InStorageData>();
		try {
			final InStorage inStorage = new InStorage();
			if (inStorageVo != null) {
				BeanUtils.copyProperties(inStorageVo, inStorage);
			}
			final List<InStorage> list = inStorageDao.findListByCondition(inStorage, inStorageVo.getEnterpriseCode());
			if (!CollectionUtils.isEmpty(list)) {
				InStorageData data = null;
				for (final InStorage item : list) {
					data = new InStorageData();
					BeanUtils.copyProperties(item, data);
					dataList.add(data);
				}
			}
		} catch (DaoException e) {
			throw new ServiceException(e);
		}
		return new Response(ResponseEnum.SUCCESS, dataList);
	}
	
	/**
	 * (non-Javadoc)
	 *@Title: findForSync
	 *@Description: 同步入库单
	 *@see com.sigmatrix.h5.service.InStorageService#findForSync(com.sigmatrix.h5.web.vo.InStorageListVo)
	 *@date 2017年9月10日 下午8:25:30
	 */
	@Override
	public Response findForSync(InStorageListVo vo) throws ServiceException {
		try {
			final String eseCode = vo.getEnterpriseCode();
			//查询主入库单
			//List<InStorage> list = inStorageDao.findForSync(eseCode, vo.getUserDealerId(),vo.getChannelType());
			List<InStorage> list = inStorageDao.findForSync(eseCode, vo.getUserDealerId(),vo.getChannelType(), vo.getUserId());
			if(Utils.isEmpty(list)){
				LOGGER.info("同步接口查询主入库单位空");
				return new Response(ResponseEnum.SUCCESS, list);
			}
			//转化为dto
			final List<InStorageData> listDto = this.convertDataList(list);
			list = null;
			final List<InStorageData> dataList = fillInStorageDto(eseCode,listDto);
			return new Response(ResponseEnum.SUCCESS, dataList);
		} catch (Exception e) {
			LOGGER.error("查询入库异常={}",e);
			throw new ServiceException(e);
		}
	}
	
	/**
	 * (non-Javadoc)
	 *@Title: update
	 *@Description: 上传
	 *@see com.sigmatrix.h5.service.InStorageService#update(com.sigmatrix.h5.web.vo.InStorageUpLoadVo)
	 *@date 2017年9月20日 下午6:21:06
	 */
	@Transactional(rollbackFor=ServiceException.class)
	public Response update(final InStorageUpLoadVo inStorageVo) throws ServiceException {
		LOGGER.info("上传接口请求={}",JSONObject.toJSONString(inStorageVo));
		final InStorageUpLoadBo upLoadBo = new InStorageUpLoadBo();//转换后码实体
		try {
			final String eseCode = inStorageVo.getEnterpriseCode();//企业code
			final Long inStorageId = inStorageVo.getInStorageId();//入库单Id
			final Integer userId = inStorageVo.getUserId();//登录用户Id
			final Integer warehouseId = inStorageVo.getWarehouseId();//仓库Id
			final Integer dealerId = inStorageVo.getDealerId();//目标经销商id
			final Integer userDealerId = inStorageVo.getUserDealerId();//发货经销商id
			//判断入库单是否存在
			final InStorage inStorage = inStorageDao.findById(inStorageId, eseCode);
			/**
			 * 做镜像，备查
			 * 
			 */
			if(CommonUtils.isNotEmpty(inStorageVo.getLat()) && CommonUtils.isNotEmpty(inStorageVo.getLng())) {
				inStorage.setLat(inStorageVo.getLat());
				inStorage.setLng(inStorageVo.getLng());
				SigmatrixLocationWrapper location2 = adminAreaService.getLocation(inStorageVo.getLat(), inStorageVo.getLng());
				inStorage.setProvince(location2.getProvince());
				inStorage.setCity(location2.getCity());
				inStorage.setCounty(location2.getCitySubOrdinate());
				inStorage.setAddress(location2.getAddress());
			}
			if(Utils.isEmpty(inStorage)){
				return new Response(Cons.RESPONSE_CODE_EXCEPTION, "入库单不存在");
			}
			
			//添加入库时间
			if (Utils.isEmpty(inStorage.getInStorageTime())) {
				inStorage.setInStorageTime(new Date());
			}
			
			//填充入库类型 手持入库
			if (Utils.isEmptyShortZero(inStorage.getInStorageType())) {
				inStorage.setInStorageType(Cons.IN_STORAGE_TYPE_PDA );
			}
			//自动收货入库是平台入库，如果平台发生入库，不能再走此接口进行手持入库
			if (inStorage.getInStorageType() == Cons.IN_STORAGE_TYPE_PLAT) {
				return new Response(Cons.RESPONSE_CODE_EXCEPTION, "不能进行混合入库");
			}
			
			upLoadBo.setInStorageType(inStorage.getInStorageType());
			
			upLoadBo.setInStorageId(inStorageId);
			upLoadBo.setOrderCode(inStorage.getOrderCode());//工单号 流水号 自动生成
			
			//查询仓库信息
			final Warehouse warehouse = warehouseDao.findById(warehouseId, eseCode);
			if(Utils.isEmpty(warehouse)){
				return new Response(Cons.RESPONSE_CODE_EXCEPTION, "仓库信息不存在");
			}
			upLoadBo.setWarehouseId(warehouse.getId());
			upLoadBo.setWarehouseCode(warehouse.getCode());
			upLoadBo.setWarehouseName(warehouse.getName());
			
			//查询目标经销商信息
			final DisDealer dealers = disDealersDao.findById(dealerId, eseCode);
			if (Utils.isEmpty(dealers)) {
				return new Response(Cons.RESPONSE_CODE_EXCEPTION, "经销商信息不存在");
			}
			upLoadBo.setDealerCode(dealers.getCode());//目标经销商code
			upLoadBo.setDealerName(dealers.getName());//目标经销名称
			

			//查询发货经销商信息
			final DisDealer dealerSend = disDealersDao.findById(userDealerId, eseCode);
			if (Utils.isEmpty(dealerSend)) {
				return new Response(Cons.RESPONSE_CODE_EXCEPTION, "发货经销商信息不存在");
			}
			upLoadBo.setOrganizationCode(dealerSend.getCode());//发货经销code
			upLoadBo.setOrganizationName(dealerSend.getName());//发货经销商名称
			
			//查询用户信息
			final DisDealerAcct disDealerAcct = disDealersAcctDao.findById(eseCode, userId);
			if(Utils.isEmpty(disDealerAcct)){
				return new Response(Cons.RESPONSE_CODE_EXCEPTION, "用户信息不存在");
			}
			upLoadBo.setUserId(disDealerAcct.getId());
			upLoadBo.setUserName(disDealerAcct.getName());
			
			//F:验证码是否所属该经销商，该仓库 （暂时不做验证或者扫码的时候做验证）TODO
			//判断是否有操作权限入库单权限 或 单独写一个接口 后续可以加上  TODO
			
			LOGGER.info("码向下级转化，并做是否已经入库验证");
			//F:大于包装码转化为包装码，小于包装码转化为单品码 ，并做是否已经入库验证
			/**
			 * 转化后的码集合  map
			 * 处理后的码集合 Map<产品id，Map<码级别，码内容>> Map<Integer,Map<Integer,List<String>>>
			 */
			//转化后的码集合 MAP
			Map<Integer,Map<Integer,List<String>>> mapPro = new HashMap<Integer,Map<Integer,List<String>>>();
			//转化后的码集合 实体
			List<InStorageUpLoadDetailBo> detailBoList = new ArrayList<InStorageUpLoadDetailBo>();
			//码向下级转换 返回错误的码集合
			Map<String,Object> codeErrMap = this.convertCode(eseCode, inStorageVo, mapPro, detailBoList);
			upLoadBo.setDetailBoList(detailBoList);
			//判断是否有错误的码
			int failNum = this.getFailNum(codeErrMap);
			int failSpNum = this.getFailSpNum(codeErrMap);
			if(failNum>0 || failSpNum >0){
				return new Response(Cons.RESPONSE_CODE_STORAGR_SUMBIT_CODE_EXCEPTION, "码信息有误", codeErrMap);
			}
			
			//判断是否超过入库单的单品和包装
			LOGGER.info("判断是否超过入库单的单品和包装");
			final InStorageNumBo numBo = this.validateNum(inStorage, mapPro);
			final Integer isOverFlag = numBo.getIsOverFlag();
			if(Cons.IN_STORAGE_OVER == isOverFlag){
				return new Response(ResponseEnum.EXCEPTION,"已超过入库单数量，实际单品数量："+numBo.getActNum()+" 实际包装数量："+numBo.getActSpNum());
			}
			
			//F:更新入库单和明细的码内容和实际数量
			LOGGER.info("更新入库单和明细的码内容和实际数量----------开始");
//			this.updateInStorage(eseCode, inStorage, userId, mapPro,isOverFlag);
			this.updateInStorageV2(eseCode, inStorage, userId, mapPro,isOverFlag,numBo);
			LOGGER.info("更新入库单和明细的码内容和实际数量----------结束");
			
			//F:更新库存的实际数量，库存明细数量
			LOGGER.info("更新库存的实际数量，库存明细数量-------------开始");
			this.updateStock(eseCode, upLoadBo);
			LOGGER.info("更新库存的实际数量，库存明细数量-------------结束");
			
			//F:记录码流转集合
			LOGGER.info("记录码流转集合----------开始");
			ThreadPool.executor.submit(() -> {
				try {
					this.writeCodeCirculation(eseCode,upLoadBo);
				} catch (Exception e) {
					LOGGER.error("入库单ID为:{}的入库单对mongo中的码进行操作失败", inStorageVo.getInStorageId(), e);
					syncTaskService.updateSyncErrorMsg(inStorageVo.getEnterpriseCode(), inStorageVo.getSyncTaskId(), 
							"入库单ID为:"+inStorageVo.getInStorageId()+"的入库单对mongo中的码进行操作失败");
				}
			});
			
			LOGGER.info("记录码流转集合----------结束");
			
			//入库成功后返回入库明细
			LOGGER.info("查询入库单明细返回");
			//入库成功后返回入库明细
			LOGGER.info("记录大数据------------开始");
			return inStorageService.findById(eseCode, inStorageId);
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}
	@Transactional(rollbackFor=ServiceException.class)
	private void updateInStorageV2(String eseCode, InStorage inStorage, Integer userId,
			Map<Integer, Map<Integer, List<String>>> mapPro, Integer isOverFlag, InStorageNumBo numBo) throws ServiceException {
		try {
			final Long inStorageId = inStorage.getId();//主入库单Id
			//入库单和明细的状态，默认进行中，数量如果相同为已完成
			Integer status = InStorageStatusEnum.PROCEED_ING.getId();
			if(Cons.IN_STORAGE_EQUAL == isOverFlag){
				status = InStorageStatusEnum.COMPLETE.getId();
			}
			
			for (Entry<Integer, Map<Integer, List<String>>> entry : mapPro.entrySet()) {
				final Integer productId = entry.getKey();//产品Id
				final Map<Integer,List<String>> mapCode = entry.getValue();//产品码内容按照级别区分
				//当前入库码量发送大数据
				int currentNum = 0;
				//更新明细 （更新码内容，实际数量）
				for(Map.Entry<Integer, List<String>> entryCode:mapCode.entrySet()){
					final Integer level = entryCode.getKey();//码级别
					final List<String> listCode = entryCode.getValue();//级别码集合
					currentNum = listCode.size();
					//查询入库单明细 （根据入库单  产品id 级别 查询）
					final List<InStorageDetail> list = inStorageDetailDao.findByCondition(eseCode, inStorageId, productId, level);
					LOGGER.info("查询入库单明细信息返回="+JSONObject.toJSONString(list));
					if(Utils.isEmpty(list)){
						throw new ServiceException("入库明细信息为空");
					}
					final InStorageDetail inDetail = list.get(0);//明细
					final Long inDetailId = inDetail.getId();//明细Id
					String operConent = inDetail.getOperateContent();//明细中的码
					if(Utils.isEmpty(operConent)) {
						operConent="";
					}
					//更新码内容 （向原码内容中添加新码）
					final StringBuilder codes = new StringBuilder();
					codes.append(operConent);
					for (String code : listCode) {
						if(operConent.contains(code)){//判断原来码内容中是否已经包含现在的码
							continue;
						}
						codes.append(code).append(",");
					}
					final String newOperConent = codes.toString();
					//计算实际数量
					final String[] codeArray = newOperConent.split(",");
					final Integer actualNum = codeArray.length;//码内容的实际数量
					//更新
					inStorageDetailDao.updateById(eseCode, inDetailId, newOperConent, actualNum,status);
					//记录入库单详情大数据日志
					keepBigLogInStorgDetail(eseCode,inDetail,currentNum,status);
				}
			}
			//更新主入库单(实际单品和包装数量、更新时间、更新人)
			//查询入库单明细计算包装和单品数量 sum
			int actualTotalNum = inStorageDetailDao.sumActualNumByLevel(eseCode, inStorageId, null);//全部数量
			int actualSpNum = inStorageDetailDao.sumActualNumByLevel(eseCode, inStorageId, Cons.PACK_LEVEL_ONE);//单品数据
			int actualNum = actualTotalNum - actualSpNum;//包装数量
			LOGGER.info("全部数量={} 包装数量={} 单品数量={}",actualTotalNum,actualNum,actualSpNum);
			//跟新主入库单
			inStorageDao.update(eseCode, inStorage, actualNum, actualSpNum,status);
			//银桥乳业记录大数据日志
			keepInstorgBigData(eseCode,inStorage,numBo,status);
		} catch (Exception e) {
			LOGGER.error("更新入库单异常",e);
			throw new ServiceException("更新入库单异常",e);
		}
	}

	private void keepBigLogInStorgDetail(String eseCode, InStorageDetail inDetail, int currentNum, Integer status) {
		LOGGER.info("主入库单详情记录大数据------------开始");
		BigDataInStorageDetail bigDataDetail = new BigDataInStorageDetail();
		BeanUtils.copyProperties(inDetail, bigDataDetail);
		bigDataDetail.setActualNum(currentNum);
		bigDataDetail.setStatus(status.shortValue());
		bigDataDetail.setEseId(enterpriseService.findByNomalByCode(eseCode).getBasicModuleId());
		BigDataLogger.loggerInfo(bigDataDetail.getLogType(), bigDataDetail);
		LOGGER.info("主入库单详情记录大数据------------结束");
	}

	/**
	 * (non-Javadoc)
	 *@Title: updateAuto
	 *@Description: 
	 *@see com.sigmatrix.h5.service.InStorageService#updateAuto(com.sigmatrix.h5.web.vo.InStorageUpLoadVo)
	 *@author gaomingkai
	 *@date 2018年1月19日 下午4:17:28
	 */
	@Override
	@Transactional(rollbackFor=ServiceException.class)
	public Response updateAuto(InStorageUpLoadVo inStorageVo) throws ServiceException {
		LOGGER.info("上传接口请求={}",JSONObject.toJSONString(inStorageVo));
		final InStorageUpLoadBo upLoadBo = new InStorageUpLoadBo();//转换后码实体
		try {
			final String eseCode = inStorageVo.getEnterpriseCode();//企业code
			final Long inStorageId = inStorageVo.getInStorageId();//入库单Id
			final Integer userId = inStorageVo.getUserId();//登录用户Id
			final Integer warehouseId = inStorageVo.getWarehouseId();//仓库Id
			final Integer dealerId = inStorageVo.getDealerId();
			
			//判断入库单是否存在
			final InStorage inStorage = inStorageDao.findById(inStorageId, eseCode);
			if(Utils.isEmpty(inStorage)){
				return new Response(Cons.RESPONSE_CODE_EXCEPTION, "入库单不存在");
			}
			
			//添加入库时间
			if (Utils.isEmpty(inStorage.getInStorageTime())) {
				inStorage.setInStorageTime(new Date());
			}
			
		/*	//填充入库类型 手持入库
			if (Utils.isEmptyShortZero(inStorage.getInStorageType())) {
				inStorage.setInStorageType(Cons.IN_STORAGE_TYPE_PDA );
			}
			//自动收货入库是平台入库，如果平台发生入库，不能再走此接口进行手持入库
			if (inStorage.getInStorageType() == Cons.IN_STORAGE_TYPE_PLAT) {
				return new Response(Cons.RESPONSE_CODE_EXCEPTION, "不能进行混合入库");
			}*/
			
			upLoadBo.setInStorageId(inStorageId);
			upLoadBo.setOrderCode(inStorage.getOrderCode());//工单号 流水号 自动生成
			
			//查询仓库信息
			final Warehouse warehouse = warehouseDao.findById(warehouseId, eseCode);
			if(Utils.isEmpty(warehouse)){
				return new Response(Cons.RESPONSE_CODE_EXCEPTION, "仓库信息不存在");
			}
			upLoadBo.setWarehouseId(warehouse.getId());
			upLoadBo.setWarehouseCode(warehouse.getCode());
			upLoadBo.setWarehouseName(warehouse.getName());
			
			//查询经销商信息
			final DisDealer dealers = disDealersDao.findById(dealerId, eseCode);
			if (Utils.isEmpty(dealers)) {
				return new Response(Cons.RESPONSE_CODE_EXCEPTION, "经销商信息不存在");
			}
			upLoadBo.setDealerCode(dealers.getCode());
			upLoadBo.setDealerName(dealers.getName());
			
			final Enterprise enterprise = enterpriseDao.findByCode(eseCode);
			if (Utils.isEmpty(enterprise)) {
				return new Response(Cons.RESPONSE_CODE_EXCEPTION, "企业信息不存在");
			}
			
			upLoadBo.setOrganizationCode(enterprise.getCode());
			upLoadBo.setOrganizationName(enterprise.getName());
			
			upLoadBo.setInStorageType(Cons.IN_STORAGE_TYPE_PLAT);
			
		/*	//查询用户信息
			final DisDealersAcct disDealersAcct = disDealersAcctDao.findById(eseCode, userId);
			if(Utils.isEmpty(disDealersAcct)){
				return new Response(Cons.RESPONSE_CODE_EXCEPTION, "用户信息不存在");
			}
			upLoadBo.setUserId(disDealersAcct.getId());
			upLoadBo.setUserName(disDealersAcct.getName());*/
			
			//F:验证码是否所属该经销商，该仓库 （暂时不做验证或者扫码的时候做验证）TODO
			//判断是否有操作权限入库单权限 或 单独写一个接口 后续可以加上  TODO
			
			LOGGER.info("码向下级转化，并做是否已经入库验证");
			//F:大于包装码转化为包装码，小于包装码转化为单品码 ，并做是否已经入库验证
			/**
			 * 转化后的码集合  map
			 * 处理后的码集合 Map<产品id，Map<码级别，码内容>> Map<Integer,Map<Integer,List<String>>>
			 */
			//转化后的码集合 MAP
			Map<Integer,Map<Integer,List<String>>> mapPro = new HashMap<Integer,Map<Integer,List<String>>>();
			//转化后的码集合 实体
			List<InStorageUpLoadDetailBo> detailBoList = new ArrayList<InStorageUpLoadDetailBo>();
			//码向下级转换 返回错误的码集合
			@SuppressWarnings("unused")
			Map<String,Object> codeErrMap = this.convertCode(eseCode, inStorageVo, mapPro, detailBoList);
			upLoadBo.setDetailBoList(detailBoList);
//			//判断是否有错误的码
//			int failNum = this.getFailNum(codeErrMap);
//			int failSpNum = this.getFailSpNum(codeErrMap);
//			if(failNum>0 || failSpNum >0){
//				return new Response(Cons.RESPONSE_CODE_STORAGR_SUMBIT_CODE_EXCEPTION, "码信息有误", codeErrMap);
//			}
//			
			//判断是否超过入库单的单品和包装
			LOGGER.info("判断是否超过入库单的单品和包装");
			final InStorageNumBo numBo = this.validateNum(inStorage, mapPro);
			@SuppressWarnings("unused")
			final Integer isOverFlag = numBo.getIsOverFlag();
//			if(Cons.IN_STORAGE_OVER == isOverFlag){
//				return new Response(ResponseEnum.EXCEPTION,"已超过入库单数量，实际单品数量："+numBo.getActNum()+" 实际包装数量："+numBo.getActSpNum());
//			}
			//主单据状态	
			Integer status = Integer.valueOf(inStorageVo.getStatus()+"");
			//F:更新入库单和明细的码内容和实际数量
			LOGGER.info("更新入库单和明细的码内容和实际数量----------开始");
//			this.updateAutoInStorage(eseCode, inStorage, userId, mapPro,status);
			this.updateAutoInStorageV2(eseCode, inStorage, userId, mapPro,status,numBo);
			LOGGER.info("更新入库单和明细的码内容和实际数量----------结束");
			
			//F:更新库存的实际数量，库存明细数量
			LOGGER.info("更新库存的实际数量，库存明细数量-------------开始");
			this.updateStock(eseCode, upLoadBo);
			LOGGER.info("更新库存的实际数量，库存明细数量-------------结束");
			
			//F:记录码流转集合
			LOGGER.info("记录码流转集合----------开始");
			ThreadPool.executor.submit(() -> {
				try {
					this.writeCodeCirculation(eseCode,upLoadBo);
				} catch (Exception e) {
					LOGGER.error("入库单ID为:{}的入库单对mongo中的码进行操作失败", inStorageVo.getInStorageId(), e);
					syncTaskService.updateSyncErrorMsg(inStorageVo.getEnterpriseCode(), inStorageVo.getSyncTaskId(), 
							"入库单ID为:"+inStorageVo.getInStorageId()+"的入库单对mongo中的码进行操作失败");
				}
			});
			
			LOGGER.info("记录码流转集合----------结束");
			
			//入库成功后返回入库明细
			LOGGER.info("查询入库单明细返回");
			return inStorageService.findById(eseCode, inStorageId);
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}
	
	@Transactional(rollbackFor=ServiceException.class)
	private void updateAutoInStorageV2(String eseCode, InStorage inStorage, Integer userId,
			Map<Integer, Map<Integer, List<String>>> mapPro, Integer status, InStorageNumBo numBo) throws ServiceException {
		try {
			final Long inStorageId = inStorage.getId();//主入库单Id
			for (Entry<Integer, Map<Integer, List<String>>> entry : mapPro.entrySet()) {
				final Integer productId = entry.getKey();//产品Id
				final Map<Integer,List<String>> mapCode = entry.getValue();//产品码内容按照级别区分
				//当前入库码量发送大数据
				int currentNum = 0;
				//更新明细 （更新码内容，实际数量）
				for(Map.Entry<Integer, List<String>> entryCode:mapCode.entrySet()){
					final Integer level = entryCode.getKey();//码级别
					final List<String> listCode = entryCode.getValue();//级别码集合
					currentNum = listCode.size();
					//查询入库单明细 （根据入库单  产品id 级别 查询）
					final List<InStorageDetail> list = inStorageDetailDao.findByCondition(eseCode, inStorageId, productId, level);
					LOGGER.info("查询入库单明细信息返回="+JSONObject.toJSONString(list));
					if(Utils.isEmpty(list)){
						throw new ServiceException("入库明细信息为空");
					}
					final InStorageDetail inDetail = list.get(0);//明细
					final Long inDetailId = inDetail.getId();//明细Id
					String operConent = inDetail.getOperateContent();//明细中的码
					if(Utils.isEmpty(operConent)) {
						operConent="";
					}
					//更新码内容 （向原码内容中添加新码）
					final StringBuilder codes = new StringBuilder();
					codes.append(operConent);
					for (String code : listCode) {
						if(operConent.contains(code)){//判断原来码内容中是否已经包含现在的码
							continue;
						}
						codes.append(code).append(",");
					}
					final String newOperConent = codes.toString();
					//计算实际数量
					final String[] codeArray = newOperConent.split(",");
					final Integer actualNum = codeArray.length;//码内容的实际数量
					//更新
					inStorageDetailDao.updateById(eseCode, inDetailId, newOperConent, actualNum,status);
					//记录入库单详情大数据日志
					keepBigLogInStorgDetail(eseCode,inDetail,currentNum,status);
				}
			}
			//更新主入库单(实际单品和包装数量、更新时间、更新人)
			//查询入库单明细计算包装和单品数量 sum
			int actualTotalNum = inStorageDetailDao.sumActualNumByLevel(eseCode, inStorageId, null);//全部数量
			int actualSpNum = inStorageDetailDao.sumActualNumByLevel(eseCode, inStorageId, Cons.PACK_LEVEL_ONE);//单品数据
			int actualNum = actualTotalNum - actualSpNum;//包装数量
			LOGGER.info("全部数量={} 包装数量={} 单品数量={}",actualTotalNum,actualNum,actualSpNum);
			//跟新主入库单
			inStorageDao.update(eseCode, inStorage, actualNum, actualSpNum,status);
			keepInstorgBigData(eseCode, inStorage, numBo, status);
		} catch (Exception e) {
			LOGGER.error("更新入库单异常",e);
			throw new ServiceException("更新入库单异常",e);
		}
	}

	private void keepInstorgBigData(String eseCode,InStorage inStorage, InStorageNumBo numBo, Integer status) {
		LOGGER.info("主入库单记录大数据------------开始");
		BigDataInStorage bigData = new BigDataInStorage();
		BeanUtils.copyProperties(inStorage, bigData);
//		bigData.setAuditTime(DateUtil.getDateSecond(inStorage.getAuditTime()));
//		bigData.setCreateTime(DateUtil.getDateSecond(inStorage.getCreateTime()));
//		bigData.setDisableTime(DateUtil.getDateSecond(inStorage.getDisableTime()));
//		bigData.setEnableTime(DateUtil.getDateSecond(inStorage.getEnableTime()));
//		bigData.setEndTime(DateUtil.getDateSecond(inStorage.getEndTime()));
//		bigData.setInStorageTime(DateUtil.getDateSecond(inStorage.getInStorageTime()));
//		bigData.setStartTime(DateUtil.getDateSecond(inStorage.getStartTime()));
//		bigData.setUpdateTime(DateUtil.getDateSecond(inStorage.getUpdateTime()));
		bigData.setStatus(status.shortValue());
		bigData.setActualNumber(numBo.getCurrentActNum());
		bigData.setActualSpNum(numBo.getCurrentActSpNum());
		bigData.setEseId(enterpriseService.findByNomalByCode(eseCode).getBasicModuleId());
		BigDataLogger.loggerInfo(bigData.getLogType(), bigData);
		LOGGER.info("主入库单记录大数据------------结束");
	}

	//验证是否超过单品和包装数量
	private InStorageNumBo validateNum(final InStorage inStorage,final Map<Integer,Map<Integer,List<String>>> mapPro){
		Integer currentActSpNum = 0 ;//当前单品数量
		Integer currentActNum = 0;//当前包装数量

		int actNum = 0;//包装数量
		if (Utils.isNotEmpty(inStorage.getActualNumber())) {
			actNum = inStorage.getActualNumber();
		}
		int actSpNum = 0;//单品数量
		if (Utils.isNotEmpty(inStorage.getActualSpNum())) {
			actSpNum = inStorage.getActualSpNum();
		}
		
		for(Map.Entry<Integer, Map<Integer,List<String>>> entry:mapPro.entrySet()){
			Map<Integer,List<String>> map = entry.getValue();
			for(Map.Entry<Integer, List<String>> entry1:map.entrySet()){
				final List<String> list = entry1.getValue();
				final Integer level = entry1.getKey();//
				if(Utils.isNotEmpty(list)){
					int num = list.size();
					if(Cons.PACK_LEVEL_ONE == level){
						currentActSpNum = currentActSpNum + num;
						actSpNum =  actSpNum + num;
					}else{
						currentActNum = currentActNum + num;
						actNum = actNum + num;
					}
				}
			}
		}
		int planSpNum = 0;//计划单品数量
		if(Utils.isNotEmpty(inStorage.getPlanSpNum())){
			planSpNum = inStorage.getPlanSpNum();
		}
		int planNum = 0;//计划包装数量
		if(Utils.isNotEmpty(inStorage.getPlanNumber())){
			planNum = inStorage.getPlanNumber();
		}
		InStorageNumBo numBo = new InStorageNumBo();
		numBo.setActNum(actNum);
		numBo.setActSpNum(actSpNum);
		numBo.setPlanNum(planNum);
		numBo.setPlanSpNum(planSpNum);
		numBo.setCurrentActNum(currentActNum);
		numBo.setCurrentActSpNum(currentActSpNum);
		Integer isOverFlag = Cons.IN_STORAGE_EQUAL;//1-相等 2-超过  3-没有超过
		
		LOGGER.info("入库单，计划单品数量={} 计划包装数量={} 实际，单品数量={} 包装数量={}",planSpNum,planNum,actSpNum,actNum);
		if(actSpNum>planSpNum || actNum>planNum){//超过 返回客户端
			LOGGER.info("已超过入库单数量，实际单品数量："+actSpNum+" 实际包装数量："+actNum);
			isOverFlag = Cons.IN_STORAGE_OVER;
		}else if(actSpNum == planSpNum || actNum == planNum){//相等 入库单修改为已完成
			
			if (actSpNum == planSpNum && actSpNum !=0 && planSpNum!=0) {
				isOverFlag = Cons.IN_STORAGE_EQUAL;
			}
			else if (actNum == planNum && actNum !=0 && planNum!=0) {
				isOverFlag = Cons.IN_STORAGE_EQUAL;
			}
			else {
				isOverFlag = Cons.IN_STORAGE_LESS;
			}
			//isOverFlag = Cons.IN_STORAGE_EQUAL;
		}else{//小于  入库单修改为进行中
			isOverFlag = Cons.IN_STORAGE_LESS;
		}
		numBo.setIsOverFlag(isOverFlag);
		return numBo;
	}
	
	/**
	 *@Title: convertCode
	 *@Description: 码向下级转换
	 *@return void
	 *@date 2017年10月12日 上午10:02:51
	 *@param eseCode
	 *@param inStorageUpLoadVo
	 *@param mapPro  Map<Integer,List<String>>>  Map<产品id,List<码内容>>>
	 *@param detailBoList
	 *@throws ServiceException
	 */
	public Map<String,Object> convertCode(final String eseCode,final InStorageUpLoadVo inStorageUpLoadVo,
			Map<Integer,Map<Integer,List<String>>> mapPro,List<InStorageUpLoadDetailBo> detailBoList) throws ServiceException{
		try {
			Map<String,Object> codeErrMap = new HashMap<String,Object>();//错误的码集合(总)
			int failNum = 0;//失败单品数量
			int failSpNum = 0;//失败包装数量
			List<QRCodeError> codeErrList = new ArrayList<QRCodeError>();//所有错误的码集合
			
			final Integer warehouseId = inStorageUpLoadVo.getWarehouseId();//仓库id
			final List<InStorageUpLoadDetailVo> detailVoList = inStorageUpLoadVo.getDetailList();//入库明细
			for (InStorageUpLoadDetailVo detailVo : detailVoList) {
				//获取产品的包装级别
				final Integer productId = detailVo.getProductId();//产品id
				final Product product = productDao.findById(productId, eseCode);
				if(Utils.isEmpty(product)||Utils.isEmpty(product.getPackLevel())){
					throw new ServiceException("产品信息不存在ID="+productId);
				}
				final Integer packLevel =  product.getPackLevel().intValue();//产品的包装级别
				final List<OperateCodeUpLoadVo> codeList = detailVo.getOperateCodeList();//一个产品下的所有码
				Map<String,Object> proCodeErrMap = new HashMap<String,Object>();//产品下错误的码集合
				//Map<码级别，List<码内容>>  请求码转化为包装和单品
				final Map<Integer,List<String>> mapCode = convertCodeList(eseCode,packLevel,warehouseId,productId, codeList,proCodeErrMap);
				//码信息放入map中
				mapPro.put(productId, mapCode);
				//码信息由Map转化为实体
				this.setDetailBo(detailBoList, product, mapCode);
				//统计错误数量
				if(Utils.isNotEmpty(proCodeErrMap)){
					int failNum1 = this.getFailNum(proCodeErrMap);
					int failSpNum1 = this.getFailSpNum(proCodeErrMap);
					List<QRCodeError> codeErrList1 = this.getCodeInfo(proCodeErrMap);
					if(Utils.isNotEmpty(codeErrList1)){
						failNum = failNum+failNum1;
						failSpNum = failSpNum+failSpNum1;
						//codeErrList.addAll(codeErrList1);
						codeErrList = codeErrList1;
					}
					
				}
			}
			
			//设置总的错误信息
			codeErrMap.put(Cons.FAIL_NUM, failNum);
			codeErrMap.put(Cons.FAIL_SP_NUM, failSpNum);
			codeErrMap.put(Cons.FAIL_CODE_LIST, codeErrList);
			LOGGER.info("codeErrMap信息"+JSONObject.toJSONString(codeErrMap));
			return codeErrMap;
		} catch (Exception e) {
			LOGGER.error("码转换处理失败",e);
			throw new ServiceException("码转换处理失败",e);
		}
		
	}
	
	//记录码流转集合
	public void writeCodeCirculation(final String eseCode,final InStorageUpLoadBo upLoadBo) throws ServiceException{
		try {
			if(Utils.isEmpty(upLoadBo) || Utils.isEmpty(upLoadBo.getDetailBoList())){
				LOGGER.error("记录码流转入库单信息为空");
				throw new ServiceException("记录码流转入库单信息为空");
			}
			final List<CodeCirculation> list = new ArrayList<CodeCirculation>();
			final List<InStorageUpLoadDetailBo> detailBoList = upLoadBo.getDetailBoList();
			//遍历明细列表
			for(InStorageUpLoadDetailBo detailBo:detailBoList){
				final List<OperateCodeUpLoadBo> operCodeList = detailBo.getOperateCodeList();//码实体集合
				if(Utils.isEmpty(operCodeList)){
					LOGGER.error("记录码流转,码集合信息为空");
					continue;
				}
				for (OperateCodeUpLoadBo operCode : operCodeList) {
					final Integer level = operCode.getLevel();//码级别
					final List<String> codeList = operCode.getCodesList();//码内容
					if(Utils.isEmpty(codeList)){
						continue;
					}
					for (String qrCode : codeList) {
						final CodeCirculation codeCir = this.setCodeCirculation(eseCode, level, qrCode, upLoadBo, detailBo);
						list.add(codeCir);
					}
				}
			}
			//codeCirculationDao.batchSave(list, getCodeCirculationName(eseCode));
			mongoTemplate.insert(list, getCodeCirculationName(eseCode));
		} catch (Exception e) {
			throw new ServiceException("记录码流转异常",e);
		}
	}
	
	private CodeCirculation setCodeCirculation(final String eseCode,final Integer level,final String qrCode,
			final InStorageUpLoadBo upLoadBo,InStorageUpLoadDetailBo detailBo) throws ServiceException{
		try {
			String urlDomain = null;
			@SuppressWarnings("unused")
			Integer status = null;
			String batchcode = null;
/*			Enterprise enterpriseFind = new Enterprise();
			enterpriseFind.setCode(eseCode);
			List<Enterprise> enterpriseList = enterpriseDao.findListByCondition(enterpriseFind);
			Enterprise enterprise = enterpriseList.get(0);*/
			//查询一级码和多级码
			if(Cons.PACK_LEVEL_ONE == level){
				final FirstLevelQRCode fLevel = firstLevelQRCodeDao.findFirstLevelQRCodeByQRCode(eseCode, qrCode);
				if(Utils.isEmpty(fLevel)){
					throw new ServiceException("一级码不存在 code="+qrCode);
				}
				urlDomain = fLevel.getCodeUrl();
				status = fLevel.getStatus();
				batchcode = fLevel.getBatchCode();
			}else{
				final MultistageLevelQRCode mLevel = multistageLevelQRCodeDao.findMultistageLevelQRCodeByQRCode(eseCode, qrCode);
				if(Utils.isEmpty(mLevel)){
					throw new ServiceException("多级码不存在 code="+qrCode);
				}
				urlDomain = mLevel.getCodeUrl();
				status = mLevel.getStatus();
				batchcode = mLevel.getBatchCode();
			}
			final CodeCirculation obj = new CodeCirculation();
			obj.setQrCode(qrCode);// 二维码SHA-256摘要后的内容
			obj.setLevel(level); // 码的包装级别
			obj.setUrlDomain(urlDomain); // 域名，包含domain和subdomain
			obj.setStatus(IN_CHANNEL_TYPE_PRO);  
			obj.setCreateTime(new Date()); // 流转记录创建时间，请使用MongoDB的Date，而不要使用ISODate
			
			obj.setCreatorName(upLoadBo.getUserName()); // 创建人名称
		    obj.setDealersType(ORGANIZATION_TYPE_DEALER); // 经销商类型
			obj.setDealersCode(upLoadBo.getDealerCode()); // 经销商代码 
			obj.setDealersName(upLoadBo.getDealerName()); // 经销商名称
	    	obj.setProductBatchCode(batchcode); // 产品生产批次，也可看做是码的生产批次
			obj.setToWarehouseCode(upLoadBo.getWarehouseCode()); // 目标仓库编码
			obj.setToWarehouseName(upLoadBo.getWarehouseName()); // 目标仓库名称
			obj.setWorkOrderId(upLoadBo.getOrderCode()); // 工单号，出入库时都有工单号
			
			obj.setProductCode(detailBo.getProductCode()); // 产品代码
			obj.setProductName(detailBo.getProductName()); // 产品名称
			if (upLoadBo.getInStorageType() ==Cons.IN_STORAGE_TYPE_PDA) {
				obj.setOrganizationType(ORGANIZATION_TYPE_DEALER);
			}
			if (upLoadBo.getInStorageType() == Cons.IN_STORAGE_TYPE_PLAT) {
				obj.setOrganizationType(ORGANIZATION_TYPE_ENT);
			}
			obj.setOrganizationCode(upLoadBo.getOrderCode());
			obj.setOrganizationName(upLoadBo.getOrganizationName());
			return obj;
		} catch (Exception e) {
			throw new ServiceException("设置码流转实体异常",e);
		}
		
	}
	
	//码信息由Map转化为实体
	private void setDetailBo(List<InStorageUpLoadDetailBo> detailBoList,
			final Product product,Map<Integer,List<String>> mapCode){
		final Integer packLevel = product.getPackLevel().intValue();//产品的包装级别
		//转化后的码集合（一个产品的码集合）
		final InStorageUpLoadDetailBo detailBo = new InStorageUpLoadDetailBo();
		detailBo.setPackLevel(packLevel);
		detailBo.setProductId(product.getId());
		detailBo.setProductCode(product.getCode());
		detailBo.setProductName(product.getName());
		//码内容
		final List<OperateCodeUpLoadBo> operateCodeList = new ArrayList<OperateCodeUpLoadBo>();
		//单品
		final OperateCodeUpLoadBo codeUpLoadBoOne = new OperateCodeUpLoadBo();
		codeUpLoadBoOne.setLevel(Cons.PACK_LEVEL_ONE);
		codeUpLoadBoOne.setCodesList(mapCode.get(Cons.PACK_LEVEL_ONE));
		operateCodeList.add(codeUpLoadBoOne);
		//包装
		final OperateCodeUpLoadBo codeUpLoadBo = new OperateCodeUpLoadBo();
		codeUpLoadBo.setLevel(packLevel);
		codeUpLoadBo.setCodesList(mapCode.get(packLevel));
		operateCodeList.add(codeUpLoadBo);
		
		detailBo.setOperateCodeList(operateCodeList);
		detailBoList.add(detailBo);
	}
	
	//获取码流转集合名称  加企业前缀
	private static String getCodeCirculationName(final String eseCode){
		return eseCode + Cons.BASIC_CODECIRCULATION_DOCUMENT_NAME;
	}
	
	/**
	 *@Title: updateInStorage
	 *@Description: 更新入库单
	 *@return void
	 *@param eseCode
	 *@param inStorageId
	 *@param userId
	 *@param mapPro
	 *@throws ServiceException
	 */
	@Transactional(rollbackFor=ServiceException.class)
	public void updateInStorage(final String eseCode,final InStorage inStorage,final Integer userId,
				final Map<Integer,Map<Integer,List<String>>> mapPro,final Integer isOverFlag) throws ServiceException{
		try {
			final Long inStorageId = inStorage.getId();//主入库单Id
			//入库单和明细的状态，默认进行中，数量如果相同为已完成
			Integer status = InStorageStatusEnum.PROCEED_ING.getId();
			if(Cons.IN_STORAGE_EQUAL == isOverFlag){
				status = InStorageStatusEnum.COMPLETE.getId();
			}
			
			for (Entry<Integer, Map<Integer, List<String>>> entry : mapPro.entrySet()) {
				final Integer productId = entry.getKey();//产品Id
				final Map<Integer,List<String>> mapCode = entry.getValue();//产品码内容按照级别区分
				//更新明细 （更新码内容，实际数量）
				for(Map.Entry<Integer, List<String>> entryCode:mapCode.entrySet()){
					final Integer level = entryCode.getKey();//码级别
					final List<String> listCode = entryCode.getValue();//级别码集合
					//查询入库单明细 （根据入库单  产品id 级别 查询）
					final List<InStorageDetail> list = inStorageDetailDao.findByCondition(eseCode, inStorageId, productId, level);
					LOGGER.info("查询入库单明细信息返回="+JSONObject.toJSONString(list));
					if(Utils.isEmpty(list)){
						throw new ServiceException("入库明细信息为空");
					}
					final InStorageDetail inDetail = list.get(0);//明细
					final Long inDetailId = inDetail.getId();//明细Id
					String operConent = inDetail.getOperateContent();//明细中的码
					if(Utils.isEmpty(operConent)) {
						operConent="";
					}
					//更新码内容 （向原码内容中添加新码）
					final StringBuilder codes = new StringBuilder();
					codes.append(operConent);
					for (String code : listCode) {
						if(operConent.contains(code)){//判断原来码内容中是否已经包含现在的码
							continue;
						}
						codes.append(code).append(",");
					}
					final String newOperConent = codes.toString();
					//计算实际数量
					final String[] codeArray = newOperConent.split(",");
					final Integer actualNum = codeArray.length;//码内容的实际数量
					//更新
					inStorageDetailDao.updateById(eseCode, inDetailId, newOperConent, actualNum,status);
					//记录大数据日志
//					keepBigLogInStorgDetail(inDetail);
				}
			}
			//更新主入库单(实际单品和包装数量、更新时间、更新人)
			//查询入库单明细计算包装和单品数量 sum
			int actualTotalNum = inStorageDetailDao.sumActualNumByLevel(eseCode, inStorageId, null);//全部数量
			int actualSpNum = inStorageDetailDao.sumActualNumByLevel(eseCode, inStorageId, Cons.PACK_LEVEL_ONE);//单品数据
			int actualNum = actualTotalNum - actualSpNum;//包装数量
			LOGGER.info("全部数量={} 包装数量={} 单品数量={}",actualTotalNum,actualNum,actualSpNum);
			//跟新主入库单
			inStorageDao.update(eseCode, inStorage, actualNum, actualSpNum,status);
			
		} catch (Exception e) {
			LOGGER.error("更新入库单异常",e);
			throw new ServiceException("更新入库单异常",e);
		}
	}
	
	
	@Transactional(rollbackFor=ServiceException.class)
	public void updateAutoInStorage(final String eseCode,final InStorage inStorage,final Integer userId,
				final Map<Integer,Map<Integer,List<String>>> mapPro,final Integer status) throws ServiceException{
		try {
			final Long inStorageId = inStorage.getId();//主入库单Id
			for (Entry<Integer, Map<Integer, List<String>>> entry : mapPro.entrySet()) {
				final Integer productId = entry.getKey();//产品Id
				final Map<Integer,List<String>> mapCode = entry.getValue();//产品码内容按照级别区分
				//更新明细 （更新码内容，实际数量）
				for(Map.Entry<Integer, List<String>> entryCode:mapCode.entrySet()){
					final Integer level = entryCode.getKey();//码级别
					final List<String> listCode = entryCode.getValue();//级别码集合
					//查询入库单明细 （根据入库单  产品id 级别 查询）
					final List<InStorageDetail> list = inStorageDetailDao.findByCondition(eseCode, inStorageId, productId, level);
					LOGGER.info("查询入库单明细信息返回="+JSONObject.toJSONString(list));
					if(Utils.isEmpty(list)){
						throw new ServiceException("入库明细信息为空");
					}
					final InStorageDetail inDetail = list.get(0);//明细
					final Long inDetailId = inDetail.getId();//明细Id
					String operConent = inDetail.getOperateContent();//明细中的码
					if(Utils.isEmpty(operConent)) {
						operConent="";
					}
					//更新码内容 （向原码内容中添加新码）
					final StringBuilder codes = new StringBuilder();
					codes.append(operConent);
					for (String code : listCode) {
						if(operConent.contains(code)){//判断原来码内容中是否已经包含现在的码
							continue;
						}
						codes.append(code).append(",");
					}
					final String newOperConent = codes.toString();
					//计算实际数量
					final String[] codeArray = newOperConent.split(",");
					final Integer actualNum = codeArray.length;//码内容的实际数量
					//更新
					inStorageDetailDao.updateById(eseCode, inDetailId, newOperConent, actualNum,status);
				}
			}
			//更新主入库单(实际单品和包装数量、更新时间、更新人)
			//查询入库单明细计算包装和单品数量 sum
			int actualTotalNum = inStorageDetailDao.sumActualNumByLevel(eseCode, inStorageId, null);//全部数量
			int actualSpNum = inStorageDetailDao.sumActualNumByLevel(eseCode, inStorageId, Cons.PACK_LEVEL_ONE);//单品数据
			int actualNum = actualTotalNum - actualSpNum;//包装数量
			LOGGER.info("全部数量={} 包装数量={} 单品数量={}",actualTotalNum,actualNum,actualSpNum);
			//跟新主入库单
			inStorageDao.update(eseCode, inStorage, actualNum, actualSpNum,status);
			
		} catch (Exception e) {
			LOGGER.error("更新入库单异常",e);
			throw new ServiceException("更新入库单异常",e);
		}
	}
	
	/**
	 *@Title: updateStock
	 *@Description: 更新库存
	 *@return void
	 *@date 2017年9月28日 下午6:21:24
	 *@param eseCode
	 *@param warehouseId
	 *@param userId
	 *@param mapPro
	 *@throws ServiceException
	 */
	@Transactional(rollbackFor=ServiceException.class)
	private void updateStock(final String eseCode,final InStorageUpLoadBo upLoadBo) throws ServiceException{
		try {
			if(Utils.isEmpty(upLoadBo) || Utils.isEmpty(upLoadBo.getDetailBoList())){
				LOGGER.error("更新库存 入库单信息为空");
				throw new ServiceException("更新库存  入库单信息为空");
			}
			final Integer userId = upLoadBo.getUserId();
			final Integer warehouseId = upLoadBo.getWarehouseId();
			List<InStorageUpLoadDetailBo> detailBoList = upLoadBo.getDetailBoList();
			final Date nowDate =  new Date();
			for (InStorageUpLoadDetailBo detailBo : detailBoList) {
				final Integer productId = detailBo.getProductId();//产品Id
				List<OperateCodeUpLoadBo> operCodeList = detailBo.getOperateCodeList();//码内容实体
				//更新明细 （更新码内容，实际数量）
				Long stockId = null;//主库存Id
				for (OperateCodeUpLoadBo operCode : operCodeList) {
					final Integer level = operCode.getLevel();//码级别
					final List<String> codeList = operCode.getCodesList();//码内容
					if(Utils.isEmpty(codeList)){
						LOGGER.info("码信息为空,不更新库存={}",JSONObject.toJSONString(operCodeList));
						continue;
					}
					//判断主库存是否存在  （如果存在，更新明细，统计后更新主库存数量；不存在，创建一条，更新明细，统计后更新库存数量）
					final List<WarehouseStock> list = warehouseStockDao.findByCondition(eseCode, warehouseId, productId, level);
					if(Utils.isNotEmpty(list)){
						final WarehouseStock stock = list.get(0);
						stockId = stock.getId();
					}else{
						WarehouseStock wStock = new WarehouseStock();
						wStock.setLevel(level);
						wStock.setChannelId(upLoadBo.getDealerId());//渠道id 经销商id
						wStock.setProductId(productId);
						wStock.setProductCode(detailBo.getProductCode());
						wStock.setWarehouseId(warehouseId);
						stockId = warehouseStockDao.insert(eseCode, wStock);
					}
					//库存明细  每个码一条记录
					final List<WarehouseStockDetails> wStockList = new ArrayList<WarehouseStockDetails>();
					for (String code : codeList) {
						WarehouseStockDetails detail = new WarehouseStockDetails();
						detail.setStockId(stockId);//主库存Id
						detail.setCode(code);//码内容
						detail.setStatus(Cons.WH_STOCK_DETAILS_STATUS_IN_STORAGE);//在库
						detail.setCreateTime(nowDate);//当前时间
						detail.setCreatorId(userId);//用户userId
						wStockList.add(detail);
					}
					//批量插入明细表
					warehouseStockDetailsDao.batchSave(wStockList, eseCode);
					
					LOGGER.info("更新库存明细");
					//查询 统计库存数量(根据主单和状态查询库存数量)
					Integer num = warehouseStockDetailsDao.countByStockId(eseCode, stockId,Cons.WH_STOCK_DETAILS_STATUS_IN_STORAGE);
					//更新主库存单（数据）
					warehouseStockDao.updateNumById(eseCode, stockId, num);
					LOGGER.info("更新主库存数量={}",num);
				}
			/*	//查询 统计库存数量(根据主单和状态查询库存数量)
				Integer num = warehouseStockDetailsDao.countByStockId(eseCode, stockId);
				//更新主库存单（数据）
				warehouseStockDao.updateNumById(eseCode, stockId, num);
				LOGGER.info("更新主库存数量={}",num);*/
			}
			
			
			//查询主库存数据是否存在两条，不存在则创建， 因为销售出库逻辑验证必须存在两条库存明细单，添加逻辑验证添加单品库存新增
			for (InStorageUpLoadDetailBo detailBo : detailBoList) {
				final Integer productId = detailBo.getProductId();//产品Id
				final List<WarehouseStock> list = warehouseStockDao.findByCondition(eseCode, warehouseId, productId, Cons.PACK_LEVEL_ONE);
				if (Utils.isEmpty(list)) {
					LOGGER.info("主库存单品级别数据为空自动创建一条记录，分销销售出库对主库存条数有要求");
					WarehouseStock wStock = new WarehouseStock();
					wStock.setLevel(Cons.PACK_LEVEL_ONE);
					wStock.setChannelId(upLoadBo.getDealerId());//渠道id 经销商id
					wStock.setProductId(productId);
					wStock.setProductCode(detailBo.getProductCode());
					wStock.setWarehouseId(warehouseId);
					wStock.setNumber(0);
					warehouseStockDao.insert(eseCode, wStock);
					LOGGER.info("创建成功主库存单品级别数据："+JSONObject.toJSONString(wStock));
				}
			}
			
			//查询主库存数据是否存在两条，不存在则创建， 因为销售出库逻辑验证必须存在两条库存明细单，添加逻辑验证添加包装库存新增
			for (InStorageUpLoadDetailBo detailBo : detailBoList) {
				final Integer productId = detailBo.getProductId();//产品Id
				final Integer packLevel = detailBo.getPackLevel();//产品包装级别
				final List<WarehouseStock> list = warehouseStockDao.findByCondition(eseCode, warehouseId, productId, packLevel);
				if (Utils.isEmpty(list)) {
					LOGGER.info("主库存包装级别数据为空自动创建一条记录，分销销售出库对主库存条数有要求");
					WarehouseStock wStock = new WarehouseStock();
					wStock.setLevel(packLevel);
					wStock.setChannelId(upLoadBo.getDealerId());//渠道id 经销商id
					wStock.setProductId(productId);
					wStock.setProductCode(detailBo.getProductCode());
					wStock.setWarehouseId(warehouseId);
					wStock.setNumber(0);
					warehouseStockDao.insert(eseCode, wStock);
					LOGGER.info("创建成功主库存包装级别数据："+JSONObject.toJSONString(wStock));
				}
			}
			
			
			
		} catch (Exception e) {
			LOGGER.error("更新库存明细异常",e);
			throw new ServiceException("更新库存明细异常");
		}
	}
	
	
	/**
	 *@Title: convertCodeList
	 *@Description: 码内容转换（大于包装码转化为包装码，小于包装码或大于单品转化为单品码）
	 *@return Map<Integer,List<String>>  Map<码级别，List<码内容>>
	 *@date 2017年9月19日 下午9:34:54
	 *@param eseCode 企业code
	 *@param packLevel 码包装级别
	 *@param codeList 同产品的码集合
	 *@param codeErrMap Map<String,Object> codeErrMap  产品下错误的码集合
	 * @throws ServiceException 
	 */
	public Map<Integer,List<String>> convertCodeList(final String eseCode,final Integer packLevel,
			final Integer warehosueId,final Integer productId,
			final List<OperateCodeUpLoadVo> codeList,final Map<String,Object> codeErrMap) throws ServiceException{
		try {
//			Map<String,Object> codeErrMap = new HashMap<String,Object>();//错误的码集合(总)
			int failNum = 0;//失败数量
			int failSpNum = 0;//失败单品数量
			List<QRCodeError> codeErrList = new ArrayList<QRCodeError>();//失败的码集合
			//Map<码级别，码内容列表>  产品下 按照级别分组
			Map<Integer,List<String>> map = new HashMap<Integer,List<String>>();
			for (OperateCodeUpLoadVo codeVo : codeList) {
				final String code = codeVo.getCode();//码内容（请求）
				Integer level = codeVo.getLevel();//当前码级别
				Integer convertLevel = null;//转换后的码级别
				boolean isConvertFlag = false;//是否需要要转化为下级
				Integer isPackOrSpFlag = 2;//转换后的码是包装码还是单品码标识  1-单品 2-包装
				//判断是否需要转化
				if(level>packLevel){//当前码大于包装转化为包装
					isConvertFlag = true;
					convertLevel = packLevel;
					LOGGER.info("转化为包装码={} 当前码级别={}",code,level);
				}else if(Cons.PACK_LEVEL_ONE < level && level < packLevel){//当前码小于包装大于单品转化为单品
					isPackOrSpFlag = 1;
					isConvertFlag = true;
					convertLevel =  Cons.PACK_LEVEL_ONE;
					LOGGER.info("转化为单品码={} 当前码级别={}",code,level);
				}else{//当前码为包装或者单品不用转换
					isConvertFlag = false;
					convertLevel = level;
					if(level == Cons.PACK_LEVEL_ONE){
						isPackOrSpFlag = 1;
					}
				}
				List<String> convertCodeList = null;//向下转化后的码集合
				if(isConvertFlag){//码需要向下转化
					final List<String> list = new ArrayList<String>();//需要向下转化的码
					list.add(code);
					//向下转换后的list
					convertCodeList = convertChilds(eseCode, convertLevel,warehosueId,productId, Cons.PACK_LEVEL_ONE, list);
					//验证码是否已经入库  返回下级码错误的码集合
					Map<String,Object> codeErrMap1 = 
							this.checkCodeInStorageList(eseCode, convertLevel, warehosueId, productId, convertCodeList);
					int failNum1 = this.getFailNum(codeErrMap1);
					if(failNum1>0){//设置下级码错误的码集合
						if(isPackOrSpFlag == 1){
							failSpNum = failSpNum+failNum1;//单品码数量累加
						}else{
							failNum = failNum+failNum1;//包装码数量累加
						}
						QRCodeError codeErr = new QRCodeError();
						codeErr.setErrorMsg("码已入库或格式不正确");
						codeErr.setProductId(productId);
						codeErr.setQrCodeLevel(level);
						codeErr.setQRCode(code);
						codeErr.setChildErrorCode(this.getCodeInfo(codeErrMap1));
						codeErrList.add(codeErr);//添加到总的错误信息list集合中
					}
					
				/*	if(Utils.isNotEmpty(convertCodeList)){
						map.get(convertLevel).addAll(convertCodeList);
						
					}else{
						LOGGER.error("转换后的码信息为空");
					}*/
					
					LOGGER.info("当前码={},码级别={},转后的码级别={}",code,level,convertLevel);
				}else{//不需要向下转化 根据级别放入list中
					convertCodeList = new ArrayList<String>();
					convertCodeList.add(code);
					//验证码是否已经入库
					QRCodeError codeErr1 = this.checkCodeInStorageOne(eseCode, convertLevel, warehosueId, productId, code);
					if(Utils.isNotEmpty(codeErr1)){
						if(isPackOrSpFlag == 1){
							failSpNum = failSpNum+1;//单品码数量累加
						}else{
							failNum = failNum+1;//包装码数量累加
						}
						codeErrList.add(codeErr1);//添加到总的错误信息list集合中
					}
					
					//验证瓶箱、盒箱、或箱垛关联  后期可能去除，暂时添加
					QRCodeError codeErr2 = this.checkCodeInStorageRelation(eseCode, convertLevel, warehosueId, productId, code);
					if(Utils.isNotEmpty(codeErr2)){
						if(isPackOrSpFlag == 1){
							failSpNum = failSpNum+1;//单品码数量累加
						}else{
							failNum = failNum+1;//包装码数量累加
						}
						codeErrList.add(codeErr2);//添加到总的错误信息list集合中
					}
				}
				
				if (Utils.isNotEmpty(convertCodeList)) {
					LOGGER.info("转化为包装码数量={}",convertCodeList.size());
					//去除重复的码
					convertCodeList = new ArrayList<String>(new HashSet<String>(convertCodeList)); 
					//放入Map中 根据级别
					if(map.containsKey(convertLevel)){
						map.get(convertLevel).addAll(convertCodeList);
					}else{
						map.put(convertLevel, convertCodeList);
					}
				}
				
			}
			//设置总的错误信息
			codeErrMap.put(Cons.FAIL_NUM, failNum);
			codeErrMap.put(Cons.FAIL_SP_NUM, failSpNum);
			codeErrMap.put(Cons.FAIL_CODE_LIST, codeErrList);
			return map;//转换后的码集合
		} catch (Exception e) {
			LOGGER.error("码包装单品转化失败");
			throw new ServiceException("码包装单品转化失败",e);
		}
		
	}
	
	/**
	 *@Title: convertChilds
	 *@Description: 循环调用查询下级码，直到下级码的leve=convertLevel
	 *@return List<String>
	 *@date 2017年9月20日 下午1:27:26
	 *@param eseCode  企业code
	 *@param convertLevel 转化为下级
	 *@param recursiveNum 递归次数
	 *@param codeList  需要转化的码内容集合  都是同一个级别的码集合
	 *@throws ServiceException
	 */
	public List<String> convertChilds(final String eseCode,final Integer convertLevel,
			final Integer warehosueId,final Integer productId,
			Integer recursiveNum,final List<String> codeList) throws ServiceException{
		try {
			List<String> retCodeList = new ArrayList<String>();
			recursiveNum++;//回调次数累加
			
			//查询多级码表获取下级码内容
			Integer childPackLevel = null;//下级码的包装级别
			for (String code : codeList) {
				LOGGER.info("查询多级码表 eseCode={} code={}",eseCode,code);
				MultistageLevelQRCode mLevel = multistageLevelQRCodeDao.findMultistageLevelQRCodeByQRCode(eseCode, code);
				
				if(Utils.isEmpty(mLevel)){
					LOGGER.error("码不存在");
					continue;
				} 
				if(Utils.isEmpty(mLevel.getChilds())){
					LOGGER.error("下级码内容为空");
					continue;
				}
				if(Utils.isEmpty(mLevel.getPackLevel())){
					LOGGER.error("码级别为空");
					continue;
				}
				
				if(Utils.isEmpty(childPackLevel)){
					childPackLevel = mLevel.getPackLevel();//下级码的包装级别
				}
				//获取下级码内容
				final String[] childs = mLevel.getChilds().split(",");
				LOGGER.info("下级码内容={}",JSONObject.toJSONString(childs));
				retCodeList.addAll(Arrays.asList(childs));
			}
			//判断级别是否和转换级别相同
			
			if((childPackLevel-1) == convertLevel){
				return retCodeList;//转换后的所有下级码
			}else{
				convertChilds(eseCode,convertLevel,warehosueId,productId,recursiveNum,retCodeList);//循环调用
			}
			
		/*	if(childPackLevel == convertLevel){
				return retCodeList;//转换后的所有下级码
			}else{
				convertChilds(eseCode,convertLevel,warehosueId,productId,recursiveNum,retCodeList);//循环调用
			}
			*/
			
			//防止死循环调用  现在产品的包装级别有超过6级 防止死循环
			if(recursiveNum > 6){
				throw new ServiceException("循环调用次数大于"+recursiveNum+"次");
			}
			return null;
		} catch (Exception e) {
			throw new ServiceException();
		}
	}
	
	/**
	 *@Title: checkCodeInStorageList
	 *@Description: 判断码是否已经入库
	 *@return Map<String,Object>
	 *@date 2017年10月12日 下午9:13:47
	 *@param eseCode
	 *@param level 
	 *@param warehosueId
	 *@param productId
	 *@param codeList 验证的码集合
	 */
	private Map<String,Object> checkCodeInStorageList(final String eseCode,final Integer level,
			final Integer warehosueId,final Integer productId, final List<String> codeList){
		int failNum = 0;//已经入库数量
		List<QRCodeError> list = new ArrayList<QRCodeError>();//已经入库的码

		for (final String code : codeList) {
			QRCodeError obj = this.checkCodeInStorageOne(eseCode, level, warehosueId, productId, code);
			if(Utils.isNotEmpty(obj)){
				failNum++;
				list.add(obj);
				LOGGER.error("码已经入库  code={}",code);
			}
		}
		return setQRCodeErrorMap(failNum, 0, list);
	}
	
	//判断单个码是否已经入库
	private QRCodeError checkCodeInStorageOne(final String eseCode,final Integer level,
			final Integer warehosueId,final Integer productId, final String code){
		   
		   //收货入库
		   Integer numCount = warehouseStockDetailsDao.countByCondition(eseCode, warehosueId, productId, code,Cons.WH_STOCK_STATUS_ALREADY_IN_STORAGE,false);
		if(numCount>0){
				QRCodeError obj = new QRCodeError();
				obj.setQRCode(code);
				obj.setQrCodeLevel(level);
				obj.setProductId(productId);
				obj.setErrorMsg("该码已经入库");
				return obj;
			}else{
				return null;
			}
	}
	
	/**
	 * 
	 *@Title: checkCodeInStorageRelation
	 *@Description://验证瓶箱、盒箱、或箱垛关联  后期可能去除，暂时添加
	 *@return QRCodeError
	 *@author gaomingkai
	 *@date 2017年10月31日 下午6:35:44
	 *@param eseCode
	 *@param level
	 *@param warehosueId
	 *@param productId
	 *@param code
	 *@return
	 */
	private QRCodeError checkCodeInStorageRelation(final String eseCode,final Integer level,
			final Integer warehosueId,final Integer productId, final String code){
		try {
			final Product product = productDao.findById(productId, eseCode);
			//产品包装级别
			final Short packLevel = product.getPackLevel();
			//存在瓶箱关联不能进行入库操作  
			final FirstLevelQRCode firstLevelQRCode = firstLevelQRCodeDao.findFirstLevelQRCodeByQRCode(eseCode, code);
			final MultistageLevelQRCode multistageLevelQRCode = multistageLevelQRCodeDao.findMultistageLevelQRCodeByQRCode(eseCode, code);
			Integer mulPackLevel = 0;//多级码表包装级别
			if (Utils.isNotEmpty(multistageLevelQRCode)) {
				mulPackLevel = multistageLevelQRCode.getPackLevel();
			}
			
			if (Utils.isNotEmpty(firstLevelQRCode)) {
				LOGGER.info("查询一级码信息：",JSONObject.toJSONString(firstLevelQRCode));
				final String firstParentCode = firstLevelQRCode.getParentCode();
				if(StringUtils.isNotBlank(firstParentCode)) {
					QRCodeError obj = new QRCodeError();
					obj.setQRCode(code);
					obj.setQrCodeLevel(level);
					obj.setProductId(productId);
					obj.setErrorMsg("当前瓶码存在瓶箱关联，不能进行操作");
					return obj;
				}
			}
			else if (Utils.isNotEmpty(multistageLevelQRCode)) {
				LOGGER.info("查询多级码信息：",JSONObject.toJSONString(multistageLevelQRCode));
				//箱码放行，如盒码绑定绑定了上级码不让入库
				if (mulPackLevel<packLevel) {
					final String multParentCode = multistageLevelQRCode.getParentCode();
					if(StringUtils.isNotBlank(multParentCode)) {
						QRCodeError obj = new QRCodeError();
						obj.setQRCode(code);
						obj.setQrCodeLevel(level);
						obj.setProductId(productId);
						obj.setErrorMsg("当前此码存在关联，不能进行操作");
						return obj;
					}
				}
				
			}
			else {
				return null;
			}
			
		} catch (DaoException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	
	}
	
	
	
	/**
	 *@Title: setQRCodeErrorMap
	 *@Description: 设置码验证失败的码信息
	 *@return Map<String,Object>
	 *@date 2017年10月12日 下午9:00:19
	 *@param failNum 包装数量
	 *@param failSpNum 单品数量
	 *@param list 码信息集合
	 */
	private Map<String,Object> setQRCodeErrorMap(final Integer failNum,
			final Integer failSpNum,final List<QRCodeError> list){
		Map<String,Object> map = new HashMap<String,Object>();
		map.put(Cons.FAIL_NUM, failNum);
		map.put(Cons.FAIL_SP_NUM, failSpNum); 
		map.put(Cons.FAIL_CODE_LIST, list);
		return map;
	}
	
	/**
	 *@Title: getFailNum
	 *@Description: 获取包装数量
	 *@return Integer
	 *@date 2017年10月12日 下午9:07:46
	 *@param map
	 */
	private Integer getFailNum(Map<String,Object> map){
		Object failNum = map.get(Cons.FAIL_NUM);
		if(Utils.isEmpty(failNum)){
			return 0;
		}else{
			return (Integer)failNum;
		}
	}
	/**
	 *@Title: getFailSpNum
	 *@Description: 获取单品数量
	 *@return Integer
	 *@date 2017年10月12日 下午9:08:00
	 *@param map
	 */
	private Integer getFailSpNum(Map<String,Object> map){
		Object  failSpNum = map.get(Cons.FAIL_SP_NUM);
		if(Utils.isEmpty(failSpNum)){
			return 0;
		}else{
			return (Integer)failSpNum;
		}
	}
	/**
	 *@Title: getCodeInfo
	 *@Description: 获取错误码集合
	 *@return List<QRCodeError>
	 *@date 2017年10月12日 下午9:08:19
	 *@param map
	 */
	@SuppressWarnings("unchecked")
	private List<QRCodeError> getCodeInfo(Map<String,Object> map){
		Object failCodeList = map.get(Cons.FAIL_CODE_LIST);
		if(Utils.isEmpty(failCodeList)){
			return null;
		}else{
			return (List<QRCodeError>)failCodeList;
		}
	}

	/**(non-Javadoc)
	 *@Title: colse
	 *@Description: 
	 *@see com.sigmatrix.sc.handheld.inter.service.InStorageService#colse(com.sigmatrix.sc.handheld.inter.web.vo.BaseInStorageVo)
	 *@author youjun
	 *@date 2017年8月23日 下午5:11:21
	 */
	@Override
	public Response colse(final BaseInStorageVo baseInStorageVo) throws ServiceException {
		final Long inStorageId = baseInStorageVo.getInStorageId();
		final String eseCode = baseInStorageVo.getEnterpriseCode();
		try {
			final InStorage inStorage = inStorageDao.findById(inStorageId, eseCode);
			if(Utils.isEmpty(inStorage)) {
				return new Response(Cons.RESPONSE_CODE_NO_DATA, "该入库单不存在");
			}
			// 判断当前入库单状态能否执行关闭操作
			final Short status = inStorage.getStatus();
			if(status==null) {
				return new Response(Cons.RESPONSE_CODE_EXISTED_DATA, "当前入库单无状态,请检查");
			}
			if(!canCloseStatusList.contains(status.intValue())) {
				final Map<Integer, String> statusMap = InStorageStatusEnum.getAll();
				StringBuilder bui = new StringBuilder()
						.append("当前入库单状态为:")
						.append(statusMap.get(status.intValue()))
						.append("不允许执行关闭操作");
				return new Response(Cons.RESPONSE_CODE_PARAM_INVALID,bui.toString());
			}
			//关闭主入库单
			inStorageDao.close(inStorageId, eseCode);
			//执行关闭明细操作
			inStorageDetailDao.closeByInStorageId(inStorageId, canCloseDetailStatusList, eseCode);
			return new Response(ResponseEnum.SUCCESS);
		} catch (DaoException e) {
			throw new ServiceException(e);
		}
	}
	
	//为主入库单设置明细
	private List<InStorageData> fillInStorageDto(final String eseCode,final List<InStorageData> dataList) throws ServiceException{
		try {
			for (InStorageData inStorageDto : dataList) {
				//为主入库单设置经销商名称
				final DisDealer disDealer = disDealersDao.findById(inStorageDto.getDealerId(), eseCode);
				if(Utils.isNotEmpty(disDealer)){
					inStorageDto.setDealerName(disDealer.getName());
				}
				//为主入库单按设置仓库名称
				final Warehouse warehouse = warehouseDao.findById(inStorageDto.getWarehouseId(), eseCode);
				if(Utils.isNotEmpty(warehouse)){
					inStorageDto.setWarehouseName(warehouse.getName());
				}
				//根据主入库单查询明细(一个主入库单有多个产品明细（每个产品可能对应两条记录（一个包装级别，一个单品级别））)
				List<InStorageDetail> detailList = inStorageDetailDao.findByInStorageId(inStorageDto.getInStorageId(), eseCode);
				//把每个产品的两条明细合并为一条记录 
				List<InStorageDetailData> detailDtoList = this.fillInStorageDetailDto(eseCode,inStorageDto,detailList);
				inStorageDto.setDetailList(detailDtoList);
			}
			return dataList;
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}
	
	/**
	 *@Title: fillInStorageDetailDto
	 *@Description: 把每个产品的两条明细合并为一条记录 
	 *@return List<InStorageDetailData>
	 *@date 2017年9月19日 上午11:23:54
	 *@param eseCode
	 *@param inStorageDto
	 *@param dataList
	 *@throws ServiceException
	 */
	private List<InStorageDetailData> fillInStorageDetailDto(final String eseCode,final InStorageData inStorageDto,
			final List<InStorageDetail> dataList) throws ServiceException{
		if(Utils.isEmpty(dataList)){
			LOGGER.info("入库单明细为空，创建入库单时，每个主单对应一个明细");//手动造的数据没有明细，不抛异常返回空
//			throw new ServiceException("入库单明细为空");
			return null;
		}
		//设置主单的包装单位和单品单位
		//每个主入库单详情中，产品的规格都是一样的 ，给主单设置产品规格的时候，从详情中取其中一个产品的规格即可
		String packUtil = null;//单品单位名称
		String singleUnit = null;//单品包装名称
		
		//按照产品对入库单明细分组，（一个产品会有多条入库单明细）
		Map<Integer,ArrayList<InStorageDetail>> map = new HashMap<Integer,ArrayList<InStorageDetail>>();
		for (InStorageDetail dto : dataList) {
			if(map.containsKey(dto.getProductId())){
				map.get(dto.getProductId()).add(dto);
			}else{
				map.put(dto.getProductId(), new ArrayList<InStorageDetail>(Arrays.asList(dto)));
			}
		}
		LOGGER.info("分组后入库单={}",JSONObject.toJSONString(map));
		
		//同产品合并为一条记录
		List<InStorageDetailData> detailDtoList = new ArrayList<InStorageDetailData>();
		//根据level把同产品的明细合并成一个
		for(Entry<Integer, ArrayList<InStorageDetail>> entry : map.entrySet()) {
			//设置明细信息
			final InStorageDetailData detailDto = new InStorageDetailData();//两条记录合并成一条实体
			final ProductData proDto = productService.findById(entry.getKey(), eseCode);//查询产品信息
			if(Utils.isEmpty(proDto)||Utils.isEmpty(proDto.getProductUnit())){
				LOGGER.info("产品或包装规格为空，产品id={}",entry.getKey());
				throw new ServiceException("根据id查询产品，产品或包装规格为空");
			}
			detailDto.setProductId(proDto.getProductId());
			detailDto.setProductCode(proDto.getProductCode());
			detailDto.setProductName(proDto.getProductName());
			detailDto.setProductUnit(new Integer(proDto.getProductUnit()));//产品包装级别
			detailDto.setPackStandard(proDto.getPackStandard());//产品级别
			
			for (InStorageDetail detail : entry.getValue()) {//数量量合并到一条记录中
				if(detail.getCodeLevel()==Cons.PACK_LEVEL_ONE){//单品
					detailDto.setActualSpNum(detail.getActualNum());//实际数量
					detailDto.setPlanSpNum(detail.getPlanNum());//计划数量
				}else{//包装
					detailDto.setActualNum(detail.getActualNum());//实际数量
					detailDto.setPlanNum(detail.getPlanNum());//计划数量
				}
			}
			detailDtoList.add(detailDto);
			
			//设置主单信息
			if(Utils.isEmpty(packUtil)){
				packUtil =  proDto.getPackUnit();
			}
			if(Utils.isEmpty(singleUnit)){
				singleUnit =  proDto.getSingleUnit();
			}
		}
		//设置主单信息 拼接主单计划数量和实际数量字符串
	    Integer planNumber = Utils.isNotEmpty(inStorageDto.getPlanNumber())?inStorageDto.getPlanNumber():0;
	    Integer planSpNum =  Utils.isNotEmpty(inStorageDto.getPlanSpNum())?inStorageDto.getPlanSpNum():0;
	    Integer actualNumber =  Utils.isNotEmpty(inStorageDto.getActualNumber())?inStorageDto.getActualNumber():0;
	    Integer actualSpNum =  Utils.isNotEmpty(inStorageDto.getActualSpNum())?inStorageDto.getActualSpNum():0;
		StringBuilder planAllNum = new StringBuilder()
				.append(planNumber).append(packUtil).append(planSpNum).append(singleUnit);
		StringBuilder actualAllNum = new StringBuilder()
				.append(actualNumber).append(packUtil).append(actualSpNum).append(singleUnit);
		inStorageDto.setActualAllNum(actualAllNum.toString());//3箱5瓶
		inStorageDto.setPlanAllNum(planAllNum.toString());
		return detailDtoList;
	}
	

	/**(non-Javadoc)
	 *@Title: getChannelTypeDesc
	 *@Description: 
	 *@see com.sigmatrix.sc.handheld.inter.service.InStorageService#getChannelTypeDesc(com.sigmatrix.sc.handheld.inter.web.vo.InStorageVo)
	 *@author youjun
	 *@date 2017年7月6日 下午3:29:17
	 */
	@Override
	public String getChannelTypeDesc(final InStorageVo inStorageVo) {
		if(inStorageVo!=null) {
			final Short channelType = inStorageVo.getChannelType();
			if(channelType!=null) {
				return this.getChannelTypeDesc(channelType);
			}
		}
		return "";
	}
	
	/**
	 *@Title: convertChildCodesArray
	 *@Description: 下级码数量转换为数组
	 *@return String[]
	 *@date 2017年10月12日 下午8:03:03
	 *@param codes
	 *@return
	 */
	private String[] convertChildCodesArray(final String codes){
		if(Utils.isEmpty(codes)){
			return null;
		}else{
			return codes.split(",");
		}
	}
	
	/**
	 *@Title: getChildCodesNum
	 *@Description: 码内容的实际数量
	 *@return int
	 *@date 2017年10月12日 下午8:08:22
	 *@param codes
	 */
	@SuppressWarnings("unused")
	private int getChildCodesNum(final String codes){
		String[] codeArray = this.convertChildCodesArray(codes);
		if(Utils.isEmpty(codeArray)){
			return 0;
		}else{
			return codeArray.length;
		}
	}
	
	/**
	 * (non-Javadoc)
	 *@Title: getChannelTypeDesc
	 *@Description: 
	 *@see com.sigmatrix.h5.service.InStorageService#getChannelTypeDesc(java.lang.Short)
	 *@date 2017年9月10日 下午6:28:16
	 */
	@Override
	public String getChannelTypeDesc(final Short channelType) {
		String channelTypeDesc = "未知入库类型";
		if(channelType!=null) {
			switch (channelType) {
			case Cons.IN_CHANNEL_TYPE_PRO:
				channelTypeDesc = "生产入库";
				break;
			case Cons.IN_CHANNEL_TYPE_RET:
				channelTypeDesc = "销售退货入库";
				break;
			case Cons.IN_CHANNEL_TYPE_OTH:
				channelTypeDesc = "其他入库";
				break;
			case Cons.IN_CHANNEL_TYPE_REC:
				channelTypeDesc = "收货入库";
				break;
			default:
				break;
			}
		}
		return channelTypeDesc;
	}

	/**
	 * (non-Javadoc)
	 *@Title: findByInStorageId
	 *@Description: 
	 *@see com.sigmatrix.h5.service.InStorageService#findByInStorageId(java.lang.Long, java.lang.String)
	 *@author gaomingkai
	 *@date 2018年1月11日 下午3:46:54
	 */
	@Override
	public InStorage findByInStorageId(Long id, String eseCode) throws ServiceException {
		// TODO Auto-generated method stub
		try {
			return inStorageDao.findById(id, eseCode);
		} catch (DaoException e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public void upload(InStorageUpLoadVo inStorageUpLoadVo) throws ServiceException {
		final int syncTaskId = inStorageUpLoadVo.getSyncTaskId();
		final long startTime = System.currentTimeMillis();
		LOGGER.info("异步任务处理任务号为{}的入库单ID为{}开始...", syncTaskId, inStorageUpLoadVo.getInStorageId());
		final String enterpriseCode = inStorageUpLoadVo.getEnterpriseCode();
		// 将任务置为处理中
		syncTaskService.updateStatus(enterpriseCode, syncTaskId, Cons.SYNC_TASK_STATUS_HANDLE_ING);
		// 开始处理
		final Response uploadResponse = update(inStorageUpLoadVo);
		// 更新处理结果
		final SyncTask updateSyncTask = new SyncTask();
		final short isSuccess = uploadResponse.getCode().equals(Cons.RESPONSE_CODE_SUCCESS)?Cons.SYNC_TASK_SUCCESS:
			Cons.SYNC_TASK_FAIL;
		updateSyncTask.setIsSuccess(isSuccess);
		updateSyncTask.setResponseCode(uploadResponse.getCode()+"");
		updateSyncTask.setResponseMsg(uploadResponse.getMessage());
		updateSyncTask.setResponseResult(JSONObject.toJSONString(uploadResponse.getData()));
		updateSyncTask.setTypeSign(inStorageUpLoadVo.getOrderCode());
		updateSyncTask.setStatus(Cons.SYNC_TASK_STATUS_HANDLE_FINISH); // 处理完成
		syncTaskService.updateByCondition(enterpriseCode, syncTaskId, 
				updateSyncTask);
		final long endTime = System.currentTimeMillis();
		LOGGER.info("异步任务处理任务号为{}的入库单ID为{}结束,花费时间:{}毫秒...", syncTaskId, 
				inStorageUpLoadVo.getInStorageId(), (endTime-startTime));
		
	}
	
	
/*	public static void main(String[] args) {
		QRCodeError  qrCodeError1 = new QRCodeError();
		qrCodeError1.setProductId(26);
		qrCodeError1.setQrCodeLevel(2);
		//qrCodeError1.setqRCode("rzKYRQmQHUF7IC5elog771rZkkjGTav-PI7Sgx1RMDA");
		qrCodeError1.setQRCode("rzKYRQmQHUF7IC5elog771rZkkjGTav-PI7Sgx1RMDA");
		qrCodeError1.setErrorMsg("该码已经入库");
		
		List<QRCodeError> childErrorCode = new ArrayList<QRCodeError>();
		childErrorCode.add(qrCodeError1);
		
		List<QRCodeError> childErrorCode2 = new ArrayList<QRCodeError>();
		childErrorCode2.addAll(childErrorCode);
		
		Map<String,Object> codeErrMap = new HashMap<>();
		//设置总的错误信息
		codeErrMap.put(Cons.FAIL_NUM, 1);
		codeErrMap.put(Cons.FAIL_SP_NUM, 0);
		codeErrMap.put(Cons.FAIL_CODE_LIST, childErrorCode2);
		Object object = codeErrMap.get("codeInfo");
		System.err.println(object.toString());
		System.out.println(JSON.toJSONString(codeErrMap));
		System.err.println(org.json.JSONObject.valueToString(codeErrMap));
		System.err.println(1);
		
		
		
	}*/

	/**
	 * (non-Javadoc)
	 *@Title: verification
	 *@Description: 
	 *@see com.sigmatrix.h5.service.InStorageService#verification(com.sigmatrix.sc.distribution.app.inter.web.vo.StorageUpLoad, java.lang.String)
	 *@author gaomingkai
	 *@date 2017年12月19日 下午5:24:48
	 */
	@Override
	public Response verification(InStorageUpLoad inStorageUpLoad) throws ServiceException {
		if (Utils.isEmpty(inStorageUpLoad)) {
			LOGGER.info("请求参数为空");
			return new Response(Cons.RESPONSE_CODE_EXCEPTION, "请求参数为空");
		}
		final List<QRCodeDetail> detailList = inStorageUpLoad.getDetailList();
		if (Utils.isEmpty(detailList)) {
			LOGGER.info("请求参数为空");
			return new Response(Cons.RESPONSE_CODE_EXCEPTION, "请求参数为空");
		}
		for (QRCodeDetail qrCodeDetail : detailList) {
			final String eseCode = qrCodeDetail.getEnterpriseCode();
			final String dealerCode = qrCodeDetail.getDealerCode();
			final String parentParentCode = qrCodeDetail.getParentCode();
			final String productCode = qrCodeDetail.getProductCode();
			final Integer tag = qrCodeDetail.getTag();
			final List<QrCode> qrCodeList = qrCodeDetail.getQrCodeList();
			if (Utils.isEmpty(dealerCode)||Utils.isEmpty(productCode)||Utils.isEmpty(qrCodeList) || Utils.isEmptyZero(tag)|| Utils.isEmpty(eseCode)) {
				LOGGER.info("请求参数为空");
				return new Response(Cons.RESPONSE_CODE_EXCEPTION, "请求参数为空");
			}
			try {
				//验证企业
				final EnterpriseData enterprise = enterpriseService.findByEnterpriseCode(eseCode);
				if (Utils.isEmpty(enterprise)) {
					LOGGER.info("企业信息不存在");
					return new Response(Cons.RESPONSE_CODE_EXCEPTION, "企业信息不存在");
				}
				//验证上级经销商  指的是上级 即跨级
				@SuppressWarnings("unused")
				Integer parentParentWarehouseId = 0;//上级经销商所绑定的默认仓库id
				@SuppressWarnings("unused")
				Integer parentParentId = 0;
				if (Utils.isNotEmpty(parentParentCode)) {
					final DisDealer parent = disDealersDao.findByCode(parentParentCode, eseCode);
					if (Utils.isEmpty(parent)) {
						LOGGER.info("所属经销商关联上级经销商信息不存在");
						return new Response(Cons.RESPONSE_CODE_EXCEPTION, "所属经销商关联上级经销商信息不存在");
					}
					parentParentId = parent.getId();
					List<WarehouseData> warehouseDatas = warehouseDao.findPartByDealerIdDefault(eseCode, parent.getId());
					if (Utils.isEmpty(warehouseDatas)) {
						LOGGER.info("所属经销商关联上级经销商未绑定的默认仓库");
						return new Response(Cons.RESPONSE_CODE_EXCEPTION, "所属经销商关联上级经销商未绑定的默认仓库");
					}
					final WarehouseData warehouseData = warehouseDatas.get(0);
					parentParentWarehouseId = warehouseData.getWarehouseId();//当前经销商所绑定的默认仓库id
					
				}
				
				//验证当前经销商
				final DisDealer dealer = disDealersDao.findByCode(dealerCode, eseCode);
				if (Utils.isEmpty(dealer)) {
					LOGGER.info("所属经销商信息不存在");
					return new Response(Cons.RESPONSE_CODE_EXCEPTION, "所属经销商信息不存在");
				}
				LOGGER.info("当前经销商:"+JSONObject.toJSONString(dealer));
				final Integer dealerId = dealer.getId();//当前经销商id
				final Integer parentId = dealer.getParentId();//直属上级经销商id
				List<WarehouseData> warehouseDatas = warehouseDao.findPartByDealerIdDefault(eseCode, dealerId);
				if (Utils.isEmpty(warehouseDatas)) {
					LOGGER.info("所属经销商未绑定的默认仓库");
					return new Response(Cons.RESPONSE_CODE_EXCEPTION, "所属经销商未绑定的默认仓库");
				}
				final WarehouseData warehouseData = warehouseDatas.get(0);
				@SuppressWarnings("unused")
				final Integer warehouseId = warehouseData.getWarehouseId();//当前经销商所绑定的默认仓库id
				

				//验证直属上级经销商
				if (Utils.isNotEmptyZero(parentId)) {
					final DisDealer parentDealer = disDealersDao.findById(parentId, eseCode);
					if (Utils.isEmpty(parentDealer)) {
						LOGGER.info("所属经销商关联直属上级经销商信息不存在");
						return new Response(Cons.RESPONSE_CODE_EXCEPTION, "所属经销商关联直属上级经销商信息不存在");
					}
				}
				//验证产品
				final Product product = productDao.findByCode(productCode, eseCode);
				if (Utils.isEmpty(product)) {
					LOGGER.info("所属产品信息不存在");
					return new Response(Cons.RESPONSE_CODE_EXCEPTION, "所属产品信息不存在");
				}
				LOGGER.info("当前产品:"+JSONObject.toJSONString(product));
				@SuppressWarnings("unused")
				final Integer productId = product.getId();
				
				@SuppressWarnings("unused")
				Integer parentWarehouseId = 0;//直属上级经销商所绑定的默认仓库id
				//一级经销商做收货入库库里还没有码数据,因此一级经销商不做码是否存在校验，二级经销商以下的做码是否存在校验
				if (Utils.isNotEmptyZero(parentId)) {
					List<WarehouseData> warehouseDataList = warehouseDao.findPartByDealerIdDefault(eseCode, parentId);
					if (Utils.isEmpty(warehouseDataList)) {
						LOGGER.info("所属经销商关联的上级经销商未绑定默认仓库");
						return new Response(Cons.RESPONSE_CODE_EXCEPTION, "所属经销商关联的上级经销商未绑定默认仓库");
					}
					final WarehouseData parentWarehouseData = warehouseDataList.get(0);
					parentWarehouseId = parentWarehouseData.getWarehouseId();
					
				}	

/*				//验证库存，即经销商是否已做收货入库
				//先验证们门店
				if (Cons.TAG_LEVEL_STORE == tag) {
					List<WarehouseStock> stockList = warehouseStockDao.findCondition(eseCode, warehouseId, productId, dealerId);
					LOGGER.info("打印门店库存记录:"+JSONObject.toJSONString(stockList));
					if (Utils.isEmpty(stockList)) {
						LOGGER.error("根据库存ID:{},产品ID:{},经销商ID:{}无法查找到门店库存记录", warehouseId, productId,dealerId);
						return new Response(Cons.RESPONSE_CODE_EXCEPTION, "门店上级经销商还未收货入库");
					}
					for (QrCode qrCode : qrCodeList) {
						final String code = qrCode.getCode();
						final Integer num = warehouseStockDetailsDao.countByCode(eseCode, code);
						if (num == 0) {
							LOGGER.info("此箱门店上级经销商还未收货入库:"+code);//也就是一级经销商还未做收货入库，箱码不存在
							return new Response(Cons.RESPONSE_CODE_EXCEPTION, "此箱门店上级经销商还未收货入库");
						}
					}	
					
				}*/
/*				//验证经销商
				if (Cons.TAG_LEVEL_DEALER == tag) {
					if (Utils.isNotEmpty(parentParentCode)) {
						List<WarehouseStock> stockList = warehouseStockDao.findCondition(eseCode, parentParentWarehouseId, productId, parentParentId);
						LOGGER.info("打印上级经销商库存记录:"+JSONObject.toJSONString(stockList));
						if (Utils.isEmpty(stockList)) {
							LOGGER.error("根据库存ID:{},产品ID:{},经销商ID:{}无法查找到上级经销商库存记录", parentParentWarehouseId, productId,parentParentId);
							return new Response(Cons.RESPONSE_CODE_EXCEPTION, "上级经销商还未收货入库");
						}
						
						for (QrCode qrCode : qrCodeList) {
							final String code = qrCode.getCode();
							final Integer num = warehouseStockDetailsDao.countByCode(eseCode, code);
							if (num == 0) {
								LOGGER.info("此箱上级经销商还未收货入库:"+code);//也就是一级经销商还未做收货入库，箱码不存在
								return new Response(Cons.RESPONSE_CODE_EXCEPTION, "此箱上级经销商还未收货入库");
							}
						}	
						
					}
					
					else {
						if (Utils.isNotEmptyZero(parentId)) {
							List<WarehouseStock> stockList = warehouseStockDao.findCondition(eseCode, parentWarehouseId, productId, parentId);
							LOGGER.info("打印上级经销商库存记录:"+JSONObject.toJSONString(stockList));
							if (Utils.isEmpty(stockList)) {
								LOGGER.error("根据库存ID:{},产品ID:{},经销商ID:{}无法查找到上级经销商库存记录", parentWarehouseId, productId,parentId);
								return new Response(Cons.RESPONSE_CODE_EXCEPTION, "直属上级经销商还未收货入库");
							}
							for (QrCode qrCode : qrCodeList) {
								final String code = qrCode.getCode();
								final Integer num = warehouseStockDetailsDao.countByCode(eseCode, code);
								if (num == 0) {
									LOGGER.info("此箱直属上级经销商还未收货入库:"+code);//也就是一级经销商还未做收货入库，箱码不存在
									return new Response(Cons.RESPONSE_CODE_EXCEPTION, "此箱直属上级经销商还未收货入库");
								}
							}	
							
						}
					}
				} */


			} catch (DaoException e) {
				throw new ServiceException("数据校验异常",e);
			}
			
		}
		return new Response(ResponseEnum.SUCCESS);
	}
	
	/**
	 * (non-Javadoc)
	 *@Title: check
	 *@Description: 
	 *@see com.sigmatrix.h5.service.InStorageService#check(com.sigmatrix.h5.web.vo.InStorageUpLoad)
	 *@author gaomingkai
	 *@date 2018年2月6日 上午9:54:47
	 */
	@Override
	public Response check(InStorageUpLoad inStorageUpLoad) throws ServiceException {
		if (Utils.isEmpty(inStorageUpLoad)) {
			LOGGER.info("请求参数为空");
			return new Response(Cons.RESPONSE_CODE_EXCEPTION, "请求参数为空");
		}
		final List<QRCodeDetail> detailList = inStorageUpLoad.getDetailList();
		if (Utils.isEmpty(detailList)) {
			LOGGER.info("请求参数为空");
			return new Response(Cons.RESPONSE_CODE_EXCEPTION, "请求参数为空");
		}
		for (QRCodeDetail qrCodeDetail : detailList) {
			final String eseCode = qrCodeDetail.getEnterpriseCode();
			final String dealerCode = qrCodeDetail.getDealerCode();
			final String parentParentCode = qrCodeDetail.getParentCode();
			final String productCode = qrCodeDetail.getProductCode();
			final Integer tag = qrCodeDetail.getTag();
			final List<QrCode> qrCodeList = qrCodeDetail.getQrCodeList();
			if (Utils.isEmpty(dealerCode)||Utils.isEmpty(productCode)||Utils.isEmpty(qrCodeList) || Utils.isEmptyZero(tag)|| Utils.isEmpty(eseCode)) {
				LOGGER.info("请求参数为空");
				return new Response(Cons.RESPONSE_CODE_EXCEPTION, "请求参数为空");
			}
			try {
				//验证企业
				final EnterpriseData enterprise = enterpriseService.findByEnterpriseCode(eseCode);
				if (Utils.isEmpty(enterprise)) {
					LOGGER.info("企业信息不存在");
					return new Response(Cons.RESPONSE_CODE_EXCEPTION, "企业信息不存在");
				}
				//验证上级经销商  指的是上级 即跨级
				@SuppressWarnings("unused")
				Integer parentParentWarehouseId = 0;//上级经销商所绑定的默认仓库id
				@SuppressWarnings("unused")
				Integer parentParentId = 0;
				if (Utils.isNotEmpty(parentParentCode)) {
					final DisDealer parent = disDealersDao.findByCode(parentParentCode, eseCode);
					if (Utils.isEmpty(parent)) {
						LOGGER.info("所属经销商关联上级经销商信息不存在");
						return new Response(Cons.RESPONSE_CODE_EXCEPTION, "所属经销商关联上级经销商信息不存在");
					}
					parentParentId = parent.getId();
					List<WarehouseData> warehouseDatas = warehouseDao.findPartByDealerIdDefault(eseCode, parent.getId());
					if (Utils.isEmpty(warehouseDatas)) {
						LOGGER.info("所属经销商关联上级经销商未绑定的默认仓库");
						return new Response(Cons.RESPONSE_CODE_EXCEPTION, "所属经销商关联上级经销商未绑定的默认仓库");
					}
					final WarehouseData warehouseData = warehouseDatas.get(0);
					parentParentWarehouseId = warehouseData.getWarehouseId();//当前经销商所绑定的默认仓库id
					
				}
				
				//验证当前经销商
				final DisDealer dealer = disDealersDao.findByCode(dealerCode, eseCode);
				if (Utils.isEmpty(dealer)) {
					LOGGER.info("所属经销商信息不存在");
					return new Response(Cons.RESPONSE_CODE_EXCEPTION, "所属经销商信息不存在");
				}
				LOGGER.info("当前经销商:"+JSONObject.toJSONString(dealer));
				final Integer dealerId = dealer.getId();//当前经销商id
				final Integer parentId = dealer.getParentId();//直属上级经销商id
				List<WarehouseData> warehouseDatas = warehouseDao.findPartByDealerIdDefault(eseCode, dealerId);
				if (Utils.isEmpty(warehouseDatas)) {
					LOGGER.info("所属经销商未绑定的默认仓库");
					return new Response(Cons.RESPONSE_CODE_EXCEPTION, "所属经销商未绑定的默认仓库");
				}
				final WarehouseData warehouseData = warehouseDatas.get(0);
				@SuppressWarnings("unused")
				final Integer warehouseId = warehouseData.getWarehouseId();//当前经销商所绑定的默认仓库id
				

				//验证直属上级经销商
				if (Utils.isNotEmptyZero(parentId)) {
					final DisDealer parentDealer = disDealersDao.findById(parentId, eseCode);
					if (Utils.isEmpty(parentDealer)) {
						LOGGER.info("所属经销商关联直属上级经销商信息不存在");
						return new Response(Cons.RESPONSE_CODE_EXCEPTION, "所属经销商关联直属上级经销商信息不存在");
					}
				}
				//验证产品
				final Product product = productDao.findByCode(productCode, eseCode);
				if (Utils.isEmpty(product)) {
					LOGGER.info("所属产品信息不存在");
					return new Response(Cons.RESPONSE_CODE_EXCEPTION, "所属产品信息不存在");
				}
				LOGGER.info("当前产品:"+JSONObject.toJSONString(product));
				@SuppressWarnings("unused")
				final Integer productId = product.getId();
				
				@SuppressWarnings("unused")
				Integer parentWarehouseId = 0;//直属上级经销商所绑定的默认仓库id
				//一级经销商做收货入库库里还没有码数据,因此一级经销商不做码是否存在校验，二级经销商以下的做码是否存在校验
				if (Utils.isNotEmptyZero(parentId)) {
					List<WarehouseData> warehouseDataList = warehouseDao.findPartByDealerIdDefault(eseCode, parentId);
					if (Utils.isEmpty(warehouseDataList)) {
						LOGGER.info("所属经销商关联的上级经销商未绑定默认仓库");
						return new Response(Cons.RESPONSE_CODE_EXCEPTION, "所属经销商关联的上级经销商未绑定默认仓库");
					}
					final WarehouseData parentWarehouseData = warehouseDataList.get(0);
					parentWarehouseId = parentWarehouseData.getWarehouseId();
					
				}	


			} catch (DaoException e) {
				throw new ServiceException("数据校验异常",e);
			}
			
		}
		return new Response(ResponseEnum.SUCCESS);
	}



	/**
	 * (non-Javadoc)
	 *@Title: receipt
	 *@Description: 
	 *@see com.sigmatrix.h5.service.InStorageService#receipt(com.sigmatrix.h5.web.vo.InStorageUpLoad)
	 *@author gaomingkai
	 *@date 2018年2月5日 下午4:06:21
	 */
	@Transactional(rollbackFor=ServiceException.class)
	public Response receipt(InStorageUpLoad inStorageUpLoad) throws ServiceException {
		try {
			//更新库存的实际数量，库存明细数量
			LOGGER.info("更新库存的实际数量，库存明细数量-------------开始");
			this.updateStock(inStorageUpLoad);
			LOGGER.info("更新库存的实际数量，库存明细数量-------------结束");
			return new Response(ResponseEnum.SUCCESS);
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}
	

	/**
	 * (non-Javadoc)
	 *@Title: goods
	 *@Description: 
	 *@see com.sigmatrix.h5.service.InStorageService#goods(com.sigmatrix.h5.web.vo.InStorageUpLoad)
	 *@author gaomingkai
	 *@date 2018年2月5日 下午4:06:26
	 */
	@Override
	public Response goods(InStorageUpLoad inStorageUpLoad) throws ServiceException {
		try {
			//创建或更新收货入库单
			this.updateGoods(inStorageUpLoad);
			
			//更新库存的实际数量，库存明细数量
			LOGGER.info("更新库存的实际数量，库存明细数量-------------开始");
			this.updateGoodStock(inStorageUpLoad);
			LOGGER.info("更新库存的实际数量，库存明细数量-------------结束");
			
			//打印码流转
			this.printCodeCirculation(inStorageUpLoad);
			
			return new Response(ResponseEnum.SUCCESS);
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

	private void printCodeCirculation(InStorageUpLoad inStorageUpLoad) throws DaoException {
		List<QRCodeDetail> detailList = inStorageUpLoad.getDetailList();
		for (QRCodeDetail qrCodeDetail : detailList) {
			//获取企业
			Enterprise enterpriseFind = new Enterprise();
			enterpriseFind.setCode(inStorageUpLoad.getEnterpriseCode());
			List<Enterprise> enterpriseList = enterpriseDao.findListByCondition(enterpriseFind);
			Enterprise enterprise = enterpriseList.get(0);
			//获取产品
			Product product = productDao.findByCode(qrCodeDetail.getProductCode(), qrCodeDetail.getEnterpriseCode());
			Integer leavel = product.getPackLevel() == null ? Cons.PACK_LEVEL_THREE:Integer.valueOf(product.getPackLevel()+"");
			//获取收货经销商
			DisDealer dealer = disDealersDao.findByCode(inStorageUpLoad.getDealerCode(), inStorageUpLoad.getEnterpriseCode());

			//获取默认仓库
			final List<WarehouseData> warehouseList = 
					warehouseDao.findPartByDealerIdDefault(inStorageUpLoad.getEnterpriseCode(), dealer.getId());
			WarehouseData warehouseData = null;
			if (Utils.isNotEmpty(warehouseList)) {
				 warehouseData = warehouseList.get(0);
			}
			
			//码流转集合
			List<CodeCirculation> circulationList = new ArrayList<>();
			
			List<QrCode> qrCodeList = qrCodeDetail.getQrCodeList();
			for (QrCode qrCode : qrCodeList) {
				final MultistageLevelQRCode mLevel = multistageLevelQRCodeDao.findMultistageLevelQRCodeByQRCode(enterprise.getCode(), qrCode.getCode());
				if(Utils.isEmpty(mLevel)){
					LOGGER.info("多级码不存在 code="+qrCode.getCode());
					continue;
				}
				final CodeCirculation obj = new CodeCirculation();
				obj.setQrCode(qrCode.getCode());// 二维码SHA-256摘要后的内容
				obj.setLevel(leavel); // 码的包装级别
				obj.setUrlDomain(mLevel.getCodeUrl()); // 域名，包含domain和subdomain
				obj.setStatus(IN_CHANNEL_TYPE_PRO);  
				obj.setCreateTime(new Date()); // 流转记录创建时间，请使用MongoDB的Date，而不要使用ISODate
			    obj.setDealersType(ORGANIZATION_TYPE_DEALER); // 经销商类型
				obj.setDealersCode(dealer.getCode()); // 经销商代码 
				obj.setDealersName(dealer.getName()); // 经销商名称
				if (Utils.isNotEmpty(warehouseData)) {
					obj.setToWarehouseCode(warehouseData.getWarehouseCode()); // 目标仓库编码
					obj.setToWarehouseName(warehouseData.getWarehouseName()); // 目标仓库名称
				}
				obj.setWorkOrderId(Cons.RECEIPT_SINGLE_NUMBER_PREFIX+inStorageUpLoad.getOrderCode()); // 工单号，出入库时都有工单号
				obj.setProductCode(product.getCode()); // 产品代码
				obj.setProductName(product.getName()); // 产品名称
			    obj.setOrganizationType(ORGANIZATION_TYPE_ENT);
				obj.setOrganizationCode(enterprise.getCode());
				obj.setOrganizationName(enterprise.getName());
				
				circulationList.add(obj);
			}
			
			mongoTemplate.insert(circulationList, getCodeCirculationName(qrCodeDetail.getEnterpriseCode()));
			
		}
	}

	private void updateGoods(InStorageUpLoad inStorageUpLoad) throws DaoException, ServiceException {
		//主入单
		InStorage inStorage = new InStorage();
		// 先行验证 入库单据号
		InStorage inStorageTemp = inStorageDao.findByOrderCode(Cons.RECEIPT_SINGLE_NUMBER_PREFIX+inStorageUpLoad.getOrderCode(), inStorageUpLoad.getEnterpriseCode());
		//如果单据已存在则进行更新操作
		if(Utils.isNotEmpty(inStorageTemp)){
			//更新主入库单处理
			int planNumber = inStorageTemp.getPlanNumber()+inStorageUpLoad.getPlanNumTotal();
			int actualNumber = inStorageTemp.getActualNumber()+inStorageUpLoad.getActualNumTotal();
			int planSpNum = inStorageTemp.getPlanSpNum();
			int actSpNum = inStorageTemp.getActualSpNum();
			Integer status = Integer.valueOf(String.valueOf(inStorageUpLoad.getStatus()));
			//更新主入库单
			inStorageDao.update(inStorageUpLoad.getEnterpriseCode(),inStorageTemp, actualNumber, actSpNum, status,planNumber, planSpNum);
			
			//更新明细入库单处理
			List<QRCodeDetail> InStorageDetailList = inStorageUpLoad.getDetailList();
			for (QRCodeDetail qRCodeDetail : InStorageDetailList) {
				final Product product = productDao.findByCode(qRCodeDetail.getProductCode(), qRCodeDetail.getEnterpriseCode());
				Integer leavel = product.getPackLevel() == null ? Cons.PACK_LEVEL_THREE:Integer.valueOf(product.getPackLevel()+"");
				final List<InStorageDetail> list = inStorageDetailDao.findByCondition(qRCodeDetail.getEnterpriseCode(), inStorageTemp.getId(), product.getId(), leavel);
				if(Utils.isEmpty(list)){
					throw new ServiceException("入库明细信息为空");
				}
				final InStorageDetail inDetail = list.get(0);//明细
				final Long inDetailId = inDetail.getId();//明细Id
				String operConent = inDetail.getOperateContent();//明细中的码
				if(Utils.isEmpty(operConent)) {
					operConent="";
				}
				//更新码内容 （向原码内容中添加新码）
				final StringBuilder codes = new StringBuilder();
				codes.append(operConent);
				List<QrCode> qrCodelist = qRCodeDetail.getQrCodeList();
				for (QrCode code : qrCodelist) {
					if(operConent.contains(code.getCode())){//判断原来码内容中是否已经包含现在的码
						continue;
					}
					codes.append(code.getCode()).append(",");
				}
				operConent = codes.toString();
				final Integer planNum = qrCodelist.size()+inDetail.getPlanNum();//码内容的实际数量
				final Integer actualNum = qrCodelist.size()+inDetail.getActualNum();//码内容的实际数量
				//更新入库单明细
				inStorageDetailDao.updateById(qRCodeDetail.getEnterpriseCode(), inDetailId, operConent, actualNum, status, planNum);
			}
			
			
		}
		//单据不存在则进行添加操作
		else {
			inStorage.setOrderCode(Cons.RECEIPT_SINGLE_NUMBER_PREFIX+inStorageUpLoad.getOrderCode());
			inStorage.setInStorageTime(new Date());
			final DisDealer dealer = disDealersDao.findByCode(inStorageUpLoad.getDealerCode(), inStorageUpLoad.getEnterpriseCode());
			inStorage.setDealerId(dealer.getId());
			inStorage.setPlanNumber(inStorageUpLoad.getPlanNumTotal());
			inStorage.setActualNumber(inStorageUpLoad.getActualNumTotal());
			inStorage.setPlanSpNum(0);
			inStorage.setActualSpNum(0);
			inStorage.setCreateTime(new Date());
			inStorage.setUpdateTime(new Date());
			inStorage.setChannelType(Cons.IN_CHANNEL_TYPE_REC);
			inStorage.setStatus(inStorageUpLoad.getStatus());
			inStorage.setInStorageTime(new Date());
			//根据经销商查询仓库
			final List<WarehouseData> warehouseList = warehouseDao.findPartByDealerIdDefault(inStorageUpLoad.getEnterpriseCode(), dealer.getId());
			WarehouseData warehouseData = warehouseList.get(0);
			inStorage.setWarehouseId(warehouseData.getWarehouseId());
			inStorage.setInStorageType(Cons.IN_STORAGE_TYPE_PDA);
			inStorage.setSourceNumber(inStorageUpLoad.getSourceWorkId());
			
			//保存主入库单
			final Long instorageId = (long) inStorageDao.save(inStorage, inStorageUpLoad.getEnterpriseCode());
			if(instorageId<=0) {
				throw new ServiceException("创建主入库单失败");
			}
			inStorage.setId(instorageId);//主入库单id
			
			//库存详情单处理
			List<InStorageDetail> detailList = new ArrayList<InStorageDetail>();
			List<QRCodeDetail> qRCodeDetailList = inStorageUpLoad.getDetailList();
			for (QRCodeDetail qRCodeDetail : qRCodeDetailList) {
				//包装详情即箱码
				final InStorageDetail detailPack = new InStorageDetail();
				detailPack.setInStorageId(instorageId);
				detailPack.setOrderCode(Cons.RECEIPT_SINGLE_NUMBER_PREFIX+inStorageUpLoad.getOrderCode());
				final Product product = productDao.findByCode(qRCodeDetail.getProductCode(), qRCodeDetail.getEnterpriseCode());
				detailPack.setProductId(product.getId());
				detailPack.setProductCode(product.getCode());
				detailPack.setCreateTime(new Date());
				int planNum = qRCodeDetail.getQrCodeList().size();
				detailPack.setPlanNum(planNum);
				int actualNum = qRCodeDetail.getQrCodeList().size();
				detailPack.setActualNum(actualNum);
				detailPack.setCodeLevel(product.getPackLevel()==null? Cons.PACK_LEVEL_THREE:Integer.valueOf(String.valueOf(product.getPackLevel())));
				detailPack.setWrapName(Cons.BOX);
				
				String operConent = "";//明细中的码
				//更新码内容 （向原码内容中添加新码）
				final StringBuilder codes = new StringBuilder();
				codes.append(operConent);
				List<QrCode> qrCodelist = qRCodeDetail.getQrCodeList();
				for (QrCode code : qrCodelist) {
					if(operConent.contains(code.getCode())){//判断原来码内容中是否已经包含现在的码
						continue;
					}
					codes.append(code.getCode()).append(",");
				}
				detailPack.setOperateContent(codes.toString());
				
				detailPack.setStatus(inStorageUpLoad.getStatus());
				
				//添加单品码
				//单品详情
				final InStorageDetail detailSp = new InStorageDetail();
				detailSp.setInStorageId(instorageId);
				detailSp.setOrderCode(Cons.RECEIPT_SINGLE_NUMBER_PREFIX+inStorageUpLoad.getOrderCode());
				detailSp.setProductId(product.getId());
				detailSp.setProductCode(product.getCode());
				detailSp.setCreateTime(new Date());
				detailSp.setPlanNum(0);
				detailSp.setActualNum(0);
				detailSp.setCodeLevel(Cons.PACK_LEVEL_ONE);
				detailSp.setWrapName(Cons.BOTTLE);
				detailSp.setStatus(Cons.IN_STATUS_HAS_IN);
				
				detailList.add(detailPack);
				detailList.add(detailSp);
				
			}
			
			// 保存入库单详情
			int result = inStorageDetailDao.batchSave(detailList, inStorageUpLoad.getEnterpriseCode());
			if (result <= 0) {
				throw new ServiceException("创建入库单明细失败");
			}


		}
	}


	
	
	/**
	 *@Title: updateStock
	 *@Description: 更新库存
	 *@return void
	 *@date 2017年9月28日 下午6:21:24
	 *@param eseCode
	 *@param warehouseId
	 *@param userId
	 *@param mapPro
	 *@throws ServiceException
	 */
	@Transactional(rollbackFor=ServiceException.class)
	private void updateStock(final InStorageUpLoad inStorageUpLoad) throws ServiceException{
		try {
			List<QRCodeDetail> detailList = inStorageUpLoad.getDetailList();
			for (QRCodeDetail qRCodeDetail : detailList) {
				final String eseCode = qRCodeDetail.getEnterpriseCode();
				final String productCode = qRCodeDetail.getProductCode();
				final String dealerCode = qRCodeDetail.getDealerCode();
				final String parentCode = qRCodeDetail.getParentCode();
				final Integer tag = qRCodeDetail.getTag();
				final Integer userId = qRCodeDetail.getUserId();
				List<QrCode> qrCodes = qRCodeDetail.getQrCodeList();
				
			
				//码实体转化为list
				List<String> codeList = new ArrayList<>();
				for (QrCode qrcode : qrCodes) {
					codeList.add(qrcode.getCode());
				}
				//码数量
				int number = codeList.size();
				LOGGER.info("操作的码数据:"+number+" 操作的码集合:"+JSONObject.toJSONString(codeList));
				//获取当前经销商
				final DisDealer dealer = disDealersDao.findByCode(dealerCode, eseCode);
				LOGGER.info("当前经销商信息={}",JSONObject.toJSONString(dealer));
				final Integer dealerId = dealer.getId();//经销商id
				final Integer parentId = dealer.getParentId();//直属上级经销商id
				DisDealer parentDealer = null;//获取直属上级经销商
				Integer parentWarehouseId = 0;//获取直属上级经销商所绑定的仓库id
				//获取直属上级经销商
				if (Utils.isNotEmptyZero(parentId)) {
					parentDealer = disDealersDao.findById(parentId, eseCode);
					LOGGER.info("当前经销商所对应的直属上级经销商信息={}",JSONObject.toJSONString(parentDealer));
					final List<WarehouseData> warehouseDataList = warehouseDao.findPartByDealerIdDefault(eseCode, parentId);
					WarehouseData warehouseData = warehouseDataList.get(0);
					parentWarehouseId = warehouseData.getWarehouseId();//当前经销商所对应的仓库
					LOGGER.info("当前经销商所对应的直属上级经销商所绑定的默认仓库信息={}",JSONObject.toJSONString(warehouseData));
					
				}
				
				DisDealer parentparentDealer = null;//获取上级经销商  可能是跨级 上上级
				Integer parentparentWarehouseId = 0;//获取上级经销商所绑定的仓库id
				Integer parentparentId = 0;//获取上级经销商  可能是跨级 上上级 id
				if (Utils.isNotEmpty(parentCode)) {
					parentparentDealer = disDealersDao.findByCode(parentCode, eseCode);
					LOGGER.info("当前经销商所对应的上级经销商信息={}",JSONObject.toJSONString(parentparentDealer));
					parentparentId = parentparentDealer.getId();
					final List<WarehouseData> warehouseDataList = warehouseDao.findPartByDealerIdDefault(eseCode, parentparentId);
					WarehouseData warehouseData = warehouseDataList.get(0);
					parentparentWarehouseId = warehouseData.getWarehouseId();//当前经销商所对应的仓库
					LOGGER.info("当前经销商所对应的直属上级经销商所绑定的默认仓库信息={}",JSONObject.toJSONString(parentparentWarehouseId));
				}
				//获取产品
				final Product product = productDao.findByCode(productCode, eseCode);
				final Integer productId = product.getId();//产品id
				
				Integer level = 0;
				//包装级别
				short packlevel =product.getPackLevel();
				if (Utils.isEmptyShortZero(packlevel)) {
					level =  Cons.PACK_LEVEL_THREE;
				}
				else {
					level = Integer.valueOf(packlevel+""); 
				}
				LOGGER.info("产品信息={}",JSONObject.toJSONString(product));

				//主库存Id
				Long stockId = null;
				//获取当前经销商所绑定的默认仓库信息
				final List<WarehouseData> warehouseDataList = warehouseDao.findPartByDealerIdDefault(eseCode, dealerId);
				WarehouseData warehouseData = warehouseDataList.get(0);
				Integer warehouseId = warehouseData.getWarehouseId();//当前经销商所对应的仓库
				LOGGER.info("当前经销商所绑定的默认仓库信息={}",JSONObject.toJSONString(warehouseData));
				
				//根据tag标识判断是经销商 2代表经销商、3代表门店
				if (tag == Cons.TAG_LEVEL_DEALER) {
					//判断主库存是否存在  （如果存在，更新明细，统计后更新主库存数量；不存在，创建一条，更新明细，统计后更新库存数量）
					WarehouseStock stock = null;
					final List<WarehouseStock> list =  warehouseStockDao.findByCondition(eseCode, warehouseId, productId, dealerId,level);
					if(Utils.isNotEmpty(list)){
						stock = list.get(0);
						stockId = stock.getId();
						number = number + stock.getNumber();
						//更新主库存单（数据）
						warehouseStockDao.updateNumById(eseCode, stockId, number);
						LOGGER.info("更新主库存数量={}",number);
					}else{
						stock = new WarehouseStock();
						stock.setLevel(level);
						stock.setChannelId(dealerId);//渠道id 经销商id
						stock.setProductId(productId);
						stock.setProductCode(product.getCode());
						stock.setWarehouseId(warehouseId);
						stock.setNumber(number);
						stockId = warehouseStockDao.insert(eseCode, stock);
						LOGGER.info("添加主库存数量={}",number);
					}
					
					//单品库存新增一条0记录(无实际意义)  平台端展示需要两条记录  一个包装一个单品
					final List<WarehouseStock> listOne = warehouseStockDao.findByCondition(eseCode, warehouseId, productId, dealerId,Cons.PACK_LEVEL_ONE);
					if (Utils.isEmpty(listOne)) {
						LOGGER.info("主库存单品级别数据为空自动创建一条记录，分销销售出库对主库存条数有要求");
						WarehouseStock wStock = new WarehouseStock();
						wStock.setLevel(Cons.PACK_LEVEL_ONE);
						wStock.setChannelId(dealerId);//渠道id 经销商id
						wStock.setProductId(productId);
						wStock.setProductCode(productCode);
						wStock.setWarehouseId(warehouseId);
						wStock.setNumber(0);
						warehouseStockDao.insert(eseCode, wStock);
						LOGGER.info("创建成功主库存单品级别数据："+JSONObject.toJSONString(wStock));
					}
					
					
					//库存明细  每个码一条记录
					final List<WarehouseStockDetails> wStockList = new ArrayList<WarehouseStockDetails>();
					for (String code : codeList) {
						WarehouseStockDetails detail = new WarehouseStockDetails();
						detail.setStockId(stockId);//主库存Id
						detail.setCode(code);//码内容
						detail.setStatus(Cons.WH_STOCK_DETAILS_STATUS_IN_STORAGE);//入库
						detail.setCreateTime(new Date());//当前时间
						detail.setCreatorId(userId);//用户userId
						wStockList.add(detail);
					}
					//批量插入明细表
					warehouseStockDetailsDao.batchSave(wStockList, eseCode);
					LOGGER.info("更新库存明细结束");
					
					
					//如果parentCode 即传递上级经销商为空，则使用当前经销商的直属父级经销商
					if (Utils.isEmpty(parentCode)) {
						//有直属上级经销商，直属上级经销商要做减
						if (Utils.isNotEmpty(parentDealer)) {
							
							this.under(eseCode, productCode, userId, codeList, number, dealerId, parentDealer,
									parentWarehouseId, product, productId, level, warehouseId, stock);	
						}
		
					}
					
					else {
						//更新上上级经销商主库存数量做减   也就是跨级扫码，比如一级经销商收货入库，2级没有扫，但是3级可以直接扫
						this.crossLevel(eseCode, productCode, userId, codeList, number, dealerId, parentparentWarehouseId,
								parentparentId, product, productId, level, warehouseId, stock);	
					}

					
				
				}
				
				//门店扫码入库
				else {
					//根据tag标识判定为门店，门店扫码只做上级经销商减库存    判断如果门店上级经销商还没做收货入库，门店也可以进行扫码，创建门店上级经销商库存记录为负数，库存明细添加码
					this.store(eseCode, productCode, userId, codeList, number, dealerId, product, productId, level,
							warehouseId);		
				}
				

			}

			
		} catch (Exception e) {
			LOGGER.error("更新库存明细异常",e);
			throw new ServiceException("更新库存明细异常");
		}
	}

	private void crossLevel(final String eseCode, final String productCode, final Integer userId, List<String> codeList,
			int number, final Integer dealerId, Integer parentparentWarehouseId, Integer parentparentId,
			final Product product, final Integer productId, Integer level, Integer warehouseId, WarehouseStock stock)
			throws DaoException {
		Long stockId = null;
		Integer amount = 0;
		//判断主库存是否存在  （如果存在，更新明细，统计后更新主库存数量；不存在，创建一条，更新明细，统计后更新库存数量）
		WarehouseStock stockUnder = null;
		final List<WarehouseStock> listUnder =  warehouseStockDao.findByCondition(eseCode, parentparentWarehouseId, productId, parentparentId,level);
		if(Utils.isNotEmpty(listUnder)){
			stockUnder = listUnder.get(0);
			stockId = stockUnder.getId();
			amount = -number + stockUnder.getNumber();
			//更新主库存单（数据）
			warehouseStockDao.updateNumById(eseCode, stockId, amount);
			final List<WarehouseStockDetails> wStockUnderList = new ArrayList<WarehouseStockDetails>();
			for (String code : codeList) {
				final Integer num = warehouseStockDetailsDao.countByCode(eseCode, code);
				//上上级经销商还未收货入库，，，库存详情表添加码数据
				if (num == 0) {
					WarehouseStockDetails detail = new WarehouseStockDetails();
					detail.setStockId(stockId);//主库存Id
					detail.setCode(code);//码内容
					detail.setStatus(Cons.WH_STOCK_DETAILS_STATUS_OUT_STORAGE);//出库
					detail.setCreateTime(new Date());//当前时间
					detail.setCreatorId(userId);//用户userId
					wStockUnderList.add(detail);
					//批量插入明细表
					warehouseStockDetailsDao.batchSave(wStockUnderList, eseCode);
					wStockUnderList.clear();
				}
				else {
					List<String> updateList = new ArrayList<>();
					updateList.add(code);
					warehouseStockDetailsDao.batchUpdateStatus(stockId, updateList, Cons.WH_STOCK_DETAILS_STATUS_OUT_STORAGE, eseCode);
					LOGGER.info("更新上上级经销商库存详情码状态={}",number);
					updateList.clear();
				}
			}
		}else{
			stockUnder = new WarehouseStock();
			stockUnder.setLevel(level);
			stockUnder.setChannelId(dealerId);//渠道id 经销商id
			stockUnder.setProductId(productId);
			stockUnder.setProductCode(product.getCode());
			stockUnder.setWarehouseId(warehouseId);
			stockUnder.setNumber(-number);
			stockId = warehouseStockDao.insert(eseCode, stock);
			LOGGER.info("添加上上级经销商主库存数量={}",number);
			
			//库存明细  每个码一条记录
			final List<WarehouseStockDetails> wStockUnderList = new ArrayList<WarehouseStockDetails>();
			for (String code : codeList) {
				WarehouseStockDetails detail = new WarehouseStockDetails();
				detail.setStockId(stockId);//主库存Id
				detail.setCode(code);//码内容
				detail.setStatus(Cons.WH_STOCK_DETAILS_STATUS_OUT_STORAGE);//出库
				detail.setCreateTime(new Date());//当前时间
				detail.setCreatorId(userId);//用户userId
				wStockUnderList.add(detail);
			}
			//批量插入明细表
			warehouseStockDetailsDao.batchSave(wStockUnderList, eseCode);
			LOGGER.info("添加上上级经销商主库存明细结束");

		}
		
		//单品库存新增一条0记录(无实际意义)  平台端展示需要两条记录  一个包装一个单品
		final List<WarehouseStock> listUnderOne = warehouseStockDao.findByCondition(eseCode, parentparentWarehouseId, productId, parentparentId,Cons.PACK_LEVEL_ONE);
		if (Utils.isEmpty(listUnderOne)) {
			LOGGER.info("上上级经销商主库存单品级别数据为空自动创建一条记录，平台端对主库存条数有要求");
			WarehouseStock wStock = new WarehouseStock();
			wStock.setLevel(Cons.PACK_LEVEL_ONE);
			wStock.setChannelId(dealerId);//渠道id 经销商id
			wStock.setProductId(productId);
			wStock.setProductCode(productCode);
			wStock.setWarehouseId(warehouseId);
			wStock.setNumber(0);
			warehouseStockDao.insert(eseCode, wStock);
			LOGGER.info("上上级经销商创建成功主库存单品级别数据："+JSONObject.toJSONString(wStock));
		}
	}

	/**
	 * 
	 *@Title: under
	 *@Description: TODO()
	 *@return void
	 *@author gaomingkai
	 *@date 2018年2月6日 上午11:00:08
	 *@param eseCode
	 *@param productCode
	 *@param userId
	 *@param codeList
	 *@param number
	 *@param dealerId
	 *@param parentDealer
	 *@param parentWarehouseId
	 *@param product
	 *@param productId
	 *@param level
	 *@param warehouseId
	 *@param stock
	 *@throws DaoException
	 */
	private void under(final String eseCode, final String productCode, final Integer userId, List<String> codeList,
			int number, final Integer dealerId, DisDealer parentDealer, Integer parentWarehouseId,
			final Product product, final Integer productId, Integer level, Integer warehouseId, WarehouseStock stock)
			throws DaoException {
		Long stockId = null;
		Integer amount = 0;
		//判断主库存是否存在  （如果存在，更新明细，统计后更新主库存数量；不存在，创建一条，更新明细，统计后更新库存数量）
		WarehouseStock stockUnder = null;
		final List<WarehouseStock> listUnder =  warehouseStockDao.findByCondition(eseCode, parentWarehouseId, productId, parentDealer.getId(),level);
		if(Utils.isNotEmpty(listUnder)){
			stockUnder = listUnder.get(0);
			stockId = stockUnder.getId();
			amount = -number + stockUnder.getNumber();
			//更新主库存单（数据）
			warehouseStockDao.updateNumById(eseCode, stockId, amount);
			final List<WarehouseStockDetails> wStockUnderList = new ArrayList<WarehouseStockDetails>();
			for (String code : codeList) {
				final Integer num = warehouseStockDetailsDao.countByCode(eseCode, code);
				if (num == 0) {
					WarehouseStockDetails detail = new WarehouseStockDetails();
					detail.setStockId(stockId);//主库存Id
					detail.setCode(code);//码内容
					detail.setStatus(Cons.WH_STOCK_DETAILS_STATUS_OUT_STORAGE);//出库
					detail.setCreateTime(new Date());//当前时间
					detail.setCreatorId(userId);//用户userId
					wStockUnderList.add(detail);
					//批量插入明细表
					warehouseStockDetailsDao.batchSave(wStockUnderList, eseCode);
					wStockUnderList.clear();
				}
				else {
					List<String> updateList = new ArrayList<>();
					updateList.add(code);
					warehouseStockDetailsDao.batchUpdateStatus(stockId, updateList, Cons.WH_STOCK_DETAILS_STATUS_OUT_STORAGE, eseCode);
					LOGGER.info("更新直属上级经销商主库存明细码状态={}",number);
					updateList.clear();
				}
			}
			
			
		}else{
			stockUnder = new WarehouseStock();
			stockUnder.setLevel(level);
			stockUnder.setChannelId(dealerId);//渠道id 经销商id
			stockUnder.setProductId(productId);
			stockUnder.setProductCode(product.getCode());
			stockUnder.setWarehouseId(warehouseId);
			stockUnder.setNumber(-number);
			stockId = warehouseStockDao.insert(eseCode, stock);
			LOGGER.info("添加直属上级经销商主库存数量={}",number);
			
			//库存明细  每个码一条记录
			final List<WarehouseStockDetails> wStockUnderList = new ArrayList<WarehouseStockDetails>();
			for (String code : codeList) {
				WarehouseStockDetails detail = new WarehouseStockDetails();
				detail.setStockId(stockId);//主库存Id
				detail.setCode(code);//码内容
				detail.setStatus(Cons.WH_STOCK_DETAILS_STATUS_OUT_STORAGE);//出库
				detail.setCreateTime(new Date());//当前时间
				detail.setCreatorId(userId);//用户userId
				wStockUnderList.add(detail);
			}
			//批量插入明细表
			warehouseStockDetailsDao.batchSave(wStockUnderList, eseCode);
			LOGGER.info("添加直属上级经销商主库存明细结束");

		}
		
		//单品库存新增一条0记录(无实际意义)  平台端展示需要两条记录  一个包装一个单品
		final List<WarehouseStock> listUnderOne = warehouseStockDao.findByCondition(eseCode, parentWarehouseId, productId, parentDealer.getId(),Cons.PACK_LEVEL_ONE);
		if (Utils.isEmpty(listUnderOne)) {
			LOGGER.info("添加直属上级经销商单品级别数据为空自动创建一条记录，平台端对对主库存条数有要求");
			WarehouseStock wStock = new WarehouseStock();
			wStock.setLevel(Cons.PACK_LEVEL_ONE);
			wStock.setChannelId(dealerId);//渠道id 经销商id
			wStock.setProductId(productId);
			wStock.setProductCode(productCode);
			wStock.setWarehouseId(warehouseId);
			wStock.setNumber(0);
			warehouseStockDao.insert(eseCode, wStock);
			LOGGER.info("添加直属上级经销商创建成功主库存单品级别数据："+JSONObject.toJSONString(wStock));
		}
	}

	/**
	 * 
	 *@Title: store
	 *@Description: TODO()
	 *@return void
	 *@author gaomingkai
	 *@date 2018年2月6日 上午10:45:59
	 *@param eseCode
	 *@param productCode
	 *@param userId
	 *@param codeList
	 *@param number
	 *@param dealerId
	 *@param product
	 *@param productId
	 *@param level
	 *@param warehouseId
	 *@throws DaoException
	 */
	private void store(final String eseCode, final String productCode, final Integer userId, List<String> codeList,
			int number, final Integer dealerId, final Product product, final Integer productId, Integer level,
			Integer warehouseId) throws DaoException {
		Long stockId = null;
		Integer amount = 0;
		//判断主库存是否存在  （如果存在，更新明细，统计后更新主库存数量；不存在，创建一条，更新明细，统计后更新库存数量）
		WarehouseStock stock = null;
		final List<WarehouseStock> list =  warehouseStockDao.findByCondition(eseCode, warehouseId, productId, dealerId,level);
		if(Utils.isNotEmpty(list)){
			stock = list.get(0);
			stockId = stock.getId();
			amount = -number + stock.getNumber();
			//更新主库存单（数据）
			warehouseStockDao.updateNumById(eseCode, stockId, amount);
			final List<WarehouseStockDetails> wStockUnderList = new ArrayList<WarehouseStockDetails>();
			for (String code : codeList) {
				final Integer num = warehouseStockDetailsDao.countByCode(eseCode, code);
				//门店上级经销商还未收货入库，码添加进去
				if (num == 0) {
					WarehouseStockDetails detail = new WarehouseStockDetails();
					detail.setStockId(stockId);//主库存Id
					detail.setCode(code);//码内容
					detail.setStatus(Cons.WH_STOCK_DETAILS_STATUS_OUT_STORAGE);//出库
					detail.setCreateTime(new Date());//当前时间
					detail.setCreatorId(userId);//用户userId
					wStockUnderList.add(detail);
					//批量插入明细表
					warehouseStockDetailsDao.batchSave(wStockUnderList, eseCode);
					wStockUnderList.clear();
				}
				else {
					List<String> updateList = new ArrayList<>();
					updateList.add(code);
					warehouseStockDetailsDao.batchUpdateStatus(stockId, updateList, Cons.WH_STOCK_DETAILS_STATUS_OUT_STORAGE, eseCode);
					LOGGER.info("更新门店直属上级经销商库存明细码状态={}",number);
					updateList.clear();
				}
			}
		}else{
			stock = new WarehouseStock();
			stock.setLevel(level);
			stock.setChannelId(dealerId);//渠道id 经销商id
			stock.setProductId(productId);
			stock.setProductCode(product.getCode());
			stock.setWarehouseId(warehouseId);
			stock.setNumber(-number);
			stockId = warehouseStockDao.insert(eseCode, stock);
			LOGGER.info("添加门店上级经销商主库存数量={}",number);
			
			//库存明细  每个码一条记录
			final List<WarehouseStockDetails> wStockList = new ArrayList<WarehouseStockDetails>();
			for (String code : codeList) {
				WarehouseStockDetails detail = new WarehouseStockDetails();
				detail.setStockId(stockId);//主库存Id
				detail.setCode(code);//码内容
				detail.setStatus(Cons.WH_STOCK_DETAILS_STATUS_OUT_STORAGE);//出库（门店上级经销商出库）
				detail.setCreateTime(new Date());//当前时间
				detail.setCreatorId(userId);//用户userId
				wStockList.add(detail);
			}
			//批量插入明细表
			warehouseStockDetailsDao.batchSave(wStockList, eseCode);
			LOGGER.info("添加门店上级经销商主存明细结束");

		}
		
		//单品库存新增一条0记录(无实际意义)  平台端展示需要两条记录  一个包装一个单品
		final List<WarehouseStock> listOne = warehouseStockDao.findByCondition(eseCode, warehouseId, productId, dealerId,Cons.PACK_LEVEL_ONE);
		if (Utils.isEmpty(listOne)) {
			LOGGER.info("门店上级经销商主库存单品级别数据为空自动创建一条记录，平台端对对主库存条数有要求");
			WarehouseStock wStock = new WarehouseStock();
			wStock.setLevel(Cons.PACK_LEVEL_ONE);
			wStock.setChannelId(dealerId);//渠道id 经销商id
			wStock.setProductId(productId);
			wStock.setProductCode(productCode);
			wStock.setWarehouseId(warehouseId);
			wStock.setNumber(0);
			warehouseStockDao.insert(eseCode, wStock);
			LOGGER.info("门店上级经销商主库存单品级别数据："+JSONObject.toJSONString(wStock));
		}
	}
	
	

	/**
	 * 
	 *@Title: updateGoodStock
	 *@Description: TODO()
	 *@return void
	 *@author gaomingkai
	 *@date 2018年2月5日 下午4:05:26
	 *@param inStorageUpLoad
	 *@throws ServiceException
	 */
	@Transactional(rollbackFor=ServiceException.class)
	private void updateGoodStock(final InStorageUpLoad inStorageUpLoad) throws ServiceException{
		try {
			List<QRCodeDetail> detailList = inStorageUpLoad.getDetailList();
			for (QRCodeDetail qRCodeDetail : detailList) {
				final String eseCode = qRCodeDetail.getEnterpriseCode();
				final String productCode = qRCodeDetail.getProductCode();
				final String dealerCode = qRCodeDetail.getDealerCode();
				final String parentCode = qRCodeDetail.getParentCode();
				final Integer tag = qRCodeDetail.getTag();
				final Integer userId = qRCodeDetail.getUserId();
				List<QrCode> qrCodes = qRCodeDetail.getQrCodeList();
				
			
				//码实体转化为list
				List<String> codeList = new ArrayList<>();
				for (QrCode qrcode : qrCodes) {
					codeList.add(qrcode.getCode());
				}
				//码数量
				int number = codeList.size();
				LOGGER.info("操作的码数据:"+number+" 操作的码集合:"+JSONObject.toJSONString(codeList));
				//获取当前经销商
				final DisDealer dealer = disDealersDao.findByCode(dealerCode, eseCode);
				LOGGER.info("当前经销商信息={}",JSONObject.toJSONString(dealer));
				final Integer dealerId = dealer.getId();//经销商id
				final Integer parentId = dealer.getParentId();//直属上级经销商id
				DisDealer parentDealer = null;//获取直属上级经销商
				Integer parentWarehouseId = 0;//获取直属上级经销商所绑定的仓库id
				//获取直属上级经销商
				if (Utils.isNotEmptyZero(parentId)) {
					parentDealer = disDealersDao.findById(parentId, eseCode);
					LOGGER.info("当前经销商所对应的直属上级经销商信息={}",JSONObject.toJSONString(parentDealer));
					final List<WarehouseData> warehouseDataList = warehouseDao.findPartByDealerIdDefault(eseCode, parentId);
					WarehouseData warehouseData = warehouseDataList.get(0);
					parentWarehouseId = warehouseData.getWarehouseId();//当前经销商所对应的仓库
					LOGGER.info("当前经销商所对应的直属上级经销商所绑定的默认仓库信息={}",JSONObject.toJSONString(warehouseData));
					
				}
				
				DisDealer parentparentDealer = null;//获取上级经销商  可能是跨级 上上级
				Integer parentparentWarehouseId = 0;//获取上级经销商所绑定的仓库id
				Integer parentparentId = 0;//获取上级经销商  可能是跨级 上上级 id
				if (Utils.isNotEmpty(parentCode)) {
					parentparentDealer = disDealersDao.findByCode(parentCode, eseCode);
					LOGGER.info("当前经销商所对应的上级经销商信息={}",JSONObject.toJSONString(parentparentDealer));
					parentparentId = parentparentDealer.getId();
					final List<WarehouseData> warehouseDataList = warehouseDao.findPartByDealerIdDefault(eseCode, parentparentId);
					WarehouseData warehouseData = warehouseDataList.get(0);
					parentparentWarehouseId = warehouseData.getWarehouseId();//当前经销商所对应的仓库
					LOGGER.info("当前经销商所对应的直属上级经销商所绑定的默认仓库信息={}",JSONObject.toJSONString(parentparentWarehouseId));
				}
				//获取产品
				final Product product = productDao.findByCode(productCode, eseCode);
				final Integer productId = product.getId();//产品id
				
				Integer level = 0;
				//包装级别
				short packlevel =product.getPackLevel();
				if (Utils.isEmptyShortZero(packlevel)) {
					level =  Cons.PACK_LEVEL_THREE;
				}
				else {
					level = Integer.valueOf(packlevel+""); 
				}
				LOGGER.info("产品信息={}",JSONObject.toJSONString(product));

				//主库存Id
				Long stockId = null;
				//获取当前经销商所绑定的默认仓库信息
				final List<WarehouseData> warehouseDataList = warehouseDao.findPartByDealerIdDefault(eseCode, dealerId);
				WarehouseData warehouseData = warehouseDataList.get(0);
				Integer warehouseId = warehouseData.getWarehouseId();//当前经销商所对应的仓库
				LOGGER.info("当前经销商所绑定的默认仓库信息={}",JSONObject.toJSONString(warehouseData));
				
				//根据tag标识判断是经销商 2代表经销商、3代表门店
				if (tag == Cons.TAG_LEVEL_DEALER) {
					//判断主库存是否存在  （如果存在，更新明细，统计后更新主库存数量；不存在，创建一条，更新明细，统计后更新库存数量）
					WarehouseStock stock = null;
					final List<WarehouseStock> list =  warehouseStockDao.findByCondition(eseCode, warehouseId, productId, dealerId,level);
					if(Utils.isNotEmpty(list)){
						stock = list.get(0);
						stockId = stock.getId();
						number = number + stock.getNumber();
						//更新主库存单（数据）
						warehouseStockDao.updateNumById(eseCode, stockId, number);
						LOGGER.info("更新主库存数量={}",number);
					}else{
						stock = new WarehouseStock();
						stock.setLevel(level);
						stock.setChannelId(dealerId);//渠道id 经销商id
						stock.setProductId(productId);
						stock.setProductCode(product.getCode());
						stock.setWarehouseId(warehouseId);
						stock.setNumber(number);
						stockId = warehouseStockDao.insert(eseCode, stock);
						LOGGER.info("添加主库存数量={}",number);
					}
					
					//单品库存新增一条0记录(无实际意义)  平台端展示需要两条记录  一个包装一个单品
					final List<WarehouseStock> listOne = warehouseStockDao.findByCondition(eseCode, warehouseId, productId, dealerId,Cons.PACK_LEVEL_ONE);
					if (Utils.isEmpty(listOne)) {
						LOGGER.info("主库存单品级别数据为空自动创建一条记录，分销销售出库对主库存条数有要求");
						WarehouseStock wStock = new WarehouseStock();
						wStock.setLevel(Cons.PACK_LEVEL_ONE);
						wStock.setChannelId(dealerId);//渠道id 经销商id
						wStock.setProductId(productId);
						wStock.setProductCode(productCode);
						wStock.setWarehouseId(warehouseId);
						wStock.setNumber(0);
						warehouseStockDao.insert(eseCode, wStock);
						LOGGER.info("创建成功主库存单品级别数据："+JSONObject.toJSONString(wStock));
					}
					
					
					//库存明细  每个码一条记录
					final List<WarehouseStockDetails> wStockList = new ArrayList<WarehouseStockDetails>();
					for (String code : codeList) {
						WarehouseStockDetails detail = new WarehouseStockDetails();
						detail.setStockId(stockId);//主库存Id
						detail.setCode(code);//码内容
						detail.setStatus(Cons.WH_STOCK_DETAILS_STATUS_IN_STORAGE);//入库
						detail.setCreateTime(new Date());//当前时间
						detail.setCreatorId(userId);//用户userId
						wStockList.add(detail);
					}
					//批量插入明细表
					warehouseStockDetailsDao.batchSave(wStockList, eseCode);
					LOGGER.info("更新库存明细结束");
					
					
					//如果parentCode 即传递上级经销商为空，则使用当前经销商的直属父级经销商
					if (Utils.isEmpty(parentCode)) {
						//有直属上级经销商，直属上级经销商要做减
						if (Utils.isNotEmpty(parentDealer)) {
							//更新直属上级经销商主库存数量做减
							warehouseStockDao.reduceStock(productId, parentWarehouseId, level, number, parentDealer.getId(), eseCode);
							//更新库存明细状态
							List<WarehouseStock> stockList = warehouseStockDao.findCondition(eseCode, parentWarehouseId, productId, parentDealer.getId());
							if(CollectionUtils.isEmpty(stockList)) {
								final StringBuilder errorMsg = new StringBuilder();
								errorMsg.append("根据库存产品批次无法查找到库存记录");
								LOGGER.info("根据库存ID:{},产品ID:{},无法查找到库存记录", warehouseId, productId);
								throw new ServiceException("根据库存产品批次无法查找到库存记录");
							}
							final Long stockIdz = stockList.get(0).getId();
							warehouseStockDetailsDao.batchUpdateStatus(stockIdz, codeList, Cons.WH_STOCK_DETAILS_STATUS_OUT_STORAGE, eseCode);
							
						}
		
					}
					
					else {
						//更新上上级经销商主库存数量做减
						warehouseStockDao.reduceStock(productId, parentparentWarehouseId, level, number, parentparentId, eseCode);
						//更新库存明细状态
						List<WarehouseStock> stockList = warehouseStockDao.findCondition(eseCode, parentparentWarehouseId, productId, parentparentId);
						if(CollectionUtils.isEmpty(stockList)) {
							final StringBuilder errorMsg = new StringBuilder();
							errorMsg.append("根据库存产品批次无法查找到库存记录");
							LOGGER.info("根据库存ID:{},产品ID:{},无法查找到库存记录", warehouseId, productId);
							throw new ServiceException("根据库存产品批次无法查找到库存记录");
						}
						final Long stockIdz = stockList.get(0).getId();
						warehouseStockDetailsDao.batchUpdateStatus(stockIdz, codeList, Cons.WH_STOCK_DETAILS_STATUS_OUT_STORAGE, eseCode);
					}

					
				
				}
		
				//根据tag标识判定为门店，只做上级经销商减库存
				else {
					//更新主库存数量  dealerId给的就是门店的上级经销商
					warehouseStockDao.reduceStock(productId, warehouseId, level, number, dealerId, eseCode);
					//更新库存明细状态
					List<WarehouseStock> stockList = warehouseStockDao.findCondition(eseCode, warehouseId, productId, dealerId);
					if(CollectionUtils.isEmpty(stockList)) {
						final StringBuilder errorMsg = new StringBuilder();
						errorMsg.append("根据库存产品批次无法查找到库存记录");
						LOGGER.info("根据库存ID:{},产品ID:{},无法查找到库存记录", warehouseId, productId);
						throw new ServiceException("根据库存产品批次无法查找到库存记录");
					}
					final Long stockIdz = stockList.get(0).getId();
					warehouseStockDetailsDao.batchUpdateStatus(stockIdz, codeList, Cons.WH_STOCK_DETAILS_STATUS_OUT_STORAGE, eseCode);
					
				}

			}

			
		} catch (Exception e) {
			LOGGER.error("更新库存明细异常",e);
			throw new ServiceException("更新库存明细异常");
		}
	}
	
	
	/**
	 * (non-Javadoc)
	 *@Title: 校验是否是一级经销商  类似酒鬼酒定制需求一级经销商不做库存的增减
	 *@Description: 
	 *@see com.sigmatrix.h5.service.InStorageService#checkdealer(com.sigmatrix.h5.web.vo.InStorageUpLoad)
	 *@author gaomingkai
	 *@date 2018年2月5日 下午5:31:06
	 */
	@Override
	public Response checkdealer(InStorageUpLoad inStorageUpLoad) throws ServiceException {
		try {
			List<QRCodeDetail> detailList = inStorageUpLoad.getDetailList();
			QRCodeDetail qrCodeDetail = detailList.get(0);
			String dealerCode = qrCodeDetail.getDealerCode();
			String enterpriseCode = qrCodeDetail.getEnterpriseCode();
			DisDealer dealer = disDealersDao.findByCode(dealerCode, enterpriseCode);
			Short level = dealer.getLevel();
			List<String> eseCodeList = new ArrayList<String>();
			String eseCode = beanConfig.getEseCode();
			LOGGER.info("获取beanConfig数据企业code ={}",eseCode);
			if(Utils.isNotEmpty(eseCode)){
				eseCodeList = Arrays.asList(eseCode.split("\\|")); 
			}
			Integer isRunning = beanConfig.getIsRunning();
			LOGGER.info("获取beanConfig数据isRunning ={}",isRunning);
			if (level == 1 && isRunning == 1 && eseCodeList.contains(enterpriseCode)) {
				LOGGER.info("经销商级别level:{}, isRunning:{}, eseCodeList集合:{}  一级经销商不做库存的增减", level, isRunning,JSONObject.toJSONString(eseCodeList));
				return new Response(ResponseEnum.SUCCESS);
			}
		} catch (Exception e) {
			LOGGER.error("校验异常",e);
			throw new ServiceException("校验异常");
		}
		return new Response(ResponseEnum.PARAM_INVALID);
	}
	
	/**
	 * (non-Javadoc)
	 *@Title: receiptInStorage
	 *@Description: 返利上传调用
	 *@see com.sigmatrix.h5.service.InStorageService#receiptInStorage(com.sigmatrix.h5.web.vo.InStorageUpLoad)
	 *@author gaomingkai 
	 *@date 2018年3月9日 上午11:24:02
	 */
	@Override
	public Response receiptInStorage(InStorageUpLoad inStorageUpLoad) throws ServiceException {
		try {
			LOGGER.info("返利上传请求参数={}",JSONObject.toJSONString(inStorageUpLoad));
			final Response validateResponse = this.verification(inStorageUpLoad);
			if(validateResponse.getCode()!=Cons.RESPONSE_CODE_SUCCESS) {
				return validateResponse;
			}
			//类似酒鬼酒定制需求一级经销商不做库存的增减
			Response checkResponse = this.checkdealer(inStorageUpLoad);
			if(checkResponse.getCode() == Cons.RESPONSE_CODE_SUCCESS) {
				return new Response(ResponseEnum.SUCCESS);
			}
			//数据处理
			final Response resultResponse = this.receipt(inStorageUpLoad);
			LOGGER.info("返利上传入库单接口返回结果={}",JSONObject.toJSONString(resultResponse));
			return resultResponse;
		} catch (Exception e) {
			LOGGER.error("返利上传入库单接口失败", e);
			return new Response(ResponseEnum.EXCEPTION,e.getMessage());
		}
	}
	


	public static void main(String[] args) {
		BeanConfig beanConfig = new BeanConfig();
		beanConfig.setIsRunning(1);
		beanConfig.setEseCode("JLJ001");
		
		System.err.println(JSON.toJSONString(beanConfig));
	}

	
}
